Folly для C++: полное руководство с примерами
1. Что это и зачем нужна
Folly (Facebook Open-source LibrarY) — это библиотека компонентов C++, разработанная и используемая в Facebook (ныне Meta). Она представляет собой набор высокопроизводительных, хорошо протестированных и современных утилит, которые заполняют пробелы в стандартной библиотеке C++ (std) и Boost. Folly не является заменой STL, а скорее её расширением, предоставляющим решения для типичных задач, возникающих при разработке крупномасштабных распределённых систем и высоконагруженных серверов.
Зачем нужна Folly? В современной C++ разработке часто требуются инструменты, которых нет в стандартной библиотеке: эффективные контейнеры с определёнными свойствами (например, F14HashMap), инструменты для асинхронного программирования (Futures), атомарные операции с оптимизированными барьерами памяти (AtomicHashMap), работа с памятью без блокировок (ConcurrentSkipList) и множество других низкоуровневых оптимизаций. Folly предоставляет всё это в виде готовых, проверенных в бою компонентов. Она особенно полезна для разработчиков, пишущих производительный серверный код, работающих с сетевыми протоколами, большими объёмами данных или требующих детерминированного поведения в многопоточных сценариях.
Ключевое преимущество Folly — её ориентация на производительность и корректность. Многие алгоритмы в Folly реализованы с использованием новейших возможностей C++ (C++14/17/20) и учитывают особенности современных процессоров (кеши, предсказание ветвлений, SIMD-инструкции). Это не просто "ещё одна библиотека", а набор инструментов, который позволяет писать код, который работает быстрее и эффективнее, чем аналогичные решения на чистом STL или Boost, особенно в условиях высокой конкурентности.
2. Установка
Folly — это библиотека на C++, поэтому она не устанавливается через pip или npm. Для её использования необходимо собрать библиотеку из исходного кода и подключить её к вашему проекту. Рекомендуемый способ — использование системы сборки CMake.
Установка через vcpkg (рекомендуется для Windows и Linux)
# Установите vcpkg, если он ещё не установлен
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh # или bootstrap-vcpkg.bat для Windows
./vcpkg integrate install
# Установите Folly
./vcpkg install folly:x64-windows # для Windows
./vcpkg install folly:x64-linux # для Linux
Установка через Conan
pip install conan
conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan
conan install folly/2023.01.30@ -r bincrafters
Сборка из исходного кода (Linux/macOS)
git clone https://github.com/facebook/folly
cd folly
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install
Подключение к проекту (CMake)
find_package(folly REQUIRED)
target_link_libraries(my_project PRIVATE Folly::folly)
3. Быстрый старт — минимальный рабочий пример
Следующий пример демонстрирует использование folly::Future для асинхронного выполнения задачи и folly::format для форматирования строк.
#include <iostream>
#include <folly/futures/Future.h>
#include <folly/Format.h>
using namespace folly;
int main() {
// Асинхронная задача: вернуть число 42
auto future = makeFuture(42);
// Цепочка обработки: умножить на 2 и преобразовать в строку
auto result = future
.thenValue([](int x) { return x * 2; })
.thenValue([](int x) { return format("Результат: {}", x); })
.get(); // Блокируем поток до получения результата
std::cout << result << std::endl; // Выведет: Результат: 84
return 0;
}
Для компиляции этого примера используйте:
g++ -std=c++17 example.cpp -lfolly -lglog -lgflags -lpthread -o example
4. Основные методы/функции/классы
4.1 folly::Future и folly::Promise
Сигнатура (упрощённо):
template <class T>
class Future {
Future& thenValue(Func&& func);
T get();
bool isReady() const;
};
template <class T>
class Promise {
void setValue(T&& value);
void setException(folly::exception_wrapper ew);
Future<T> getFuture();
};
Что делает: Реализует паттерн Promise/Future для асинхронного программирования. Promise — поставщик результата, Future — потребитель. Позволяет строить цепочки асинхронных операций без блокировок.
Пример:
#include <folly/futures/Future.h>
#include <iostream>
#include <thread>
using namespace folly;
int main() {
Promise<int> promise;
Future<int> future = promise.getFuture();
std::thread producer([&promise]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
promise.setValue(100);
});
// Асинхронно обрабатываем результат
future.thenValue([](int val) {
std::cout << "Получено: " << val << std::endl;
});
producer.join();
// Ждём завершения всех Future (в реальном коде используйте EventBase)
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
4.2 folly::F14HashMap и folly::F14FastSet
Сигнатура:
template <class Key, class Value, class Hash = ...>
class F14HashMap {
Value& operator[](const Key& key);
std::pair<iterator, bool> insert(const value_type& value);
iterator find(const Key& key);
size_t erase(const Key& key);
};
template <class Key, class Hash = ...>
class F14FastSet {
std::pair<iterator, bool> insert(const Key& key);
bool contains(const Key& key) const;
};
Что делает: Высокопроизводительные хэш-таблицы, оптимизированные для современных процессоров. Используют технику "swiss table" (Google's SwissTable), что обеспечивает высокую скорость вставки, поиска и удаления, особенно при работе с большими объёмами данных.
Пример:
#include <folly/container/F14Map.h>
#include <folly/container/F14Set.h>
#include <string>
#include <iostream>
int main() {
folly::F14FastMap<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
auto it = ages.find("Alice");
if (it != ages.end()) {
std::cout << "Alice's age: " << it->second << std::endl;
}
folly::F14FastSet<int> unique_numbers;
unique_numbers.insert(1);
unique_numbers.insert(2);
unique_numbers.insert(1); // Не добавится
std::cout << "Set contains 2: " << unique_numbers.contains(2) << std::endl;
return 0;
}
4.3 folly::fbstring
Сигнатура:
class fbstring {
fbstring(const char* s);
fbstring(const fbstring& other);
const char* c_str() const;
size_t size() const;
fbstring& operator+=(const fbstring& other);
int compare(const fbstring& other) const;
};
Что делает: Альтернатива std::string с улучшенной производительностью. Использует Small String Optimization (SSO) с большим буфером (до 23 символов на 64-битных системах) и оптимизированные алгоритмы для работы с памятью.
Пример:
#include <folly/FBString.h>
#include <iostream>
int main() {
folly::fbstring hello = "Hello, ";
folly::fbstring world = "World!";
hello += world;
std::cout << hello << std::endl; // Hello, World!
folly::fbstring small = "Hi"; // Хранится в стеке (SSO)
folly::fbstring large = "This is a very long string that exceeds the SSO buffer"; // Хранится в куче
std::cout << "Small size: " << small.size() << std::endl;
std::cout << "Large size: " << large.size() << std::endl;
return 0;
}
4.4 folly::format
Сигнатура:
template <class... Args>
fbstring format(fbstring fmt, Args&&... args);
template <class... Args>
void formatTo(fbstring& output, fbstring fmt, Args&&... args);
Что делает: Безопасная и эффективная альтернатива sprintf и std::ostringstream. Поддерживает позиционные аргументы, спецификаторы формата (как в Python) и работает быстрее, чем std::format (C++20) в некоторых сценариях.
Пример:
#include <folly/Format.h>
#include <iostream>
int main() {
std::string name = "Alice";
int age = 30;
double salary = 75000.50;
// Простое форматирование
auto result = folly::format("Name: {}, Age: {}, Salary: {:.2f}", name, age, salary);
std::cout << result << std::endl;
// Позиционные аргументы
auto result2 = folly::format("{1} is {0} years old", age, name);
std::cout << result2 << std::endl;
// Форматирование в существующую строку
folly::fbstring output;
folly::formatTo(output, "Hello, {}!", "World");
std::cout << output << std::endl;
return 0;
}
4.5 folly::AtomicHashMap
Сигнатура (упрощённо):
template <class Key, class Value, class Hash = ...>
class AtomicHashMap {
bool insert(const Key& key, const Value& value);
bool find(const Key& key, Value& value_out) const;
Value* get(const Key& key);
size_t size() const;
};
Что делает: Потокобезопасная хэш-таблица без блокировок (lock-free). Оптимизирована для сценариев с интенсивной записью из нескольких потоков. Использует атомарные операции и механизм "compare-and-swap" (CAS).
Пример:
#include <folly/AtomicHashMap.h>
#include <iostream>
#include <thread>
#include <vector>
int main() {
folly::AtomicHashMap<int, int> map(1024); // Начальный размер
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&map, i]() {
for (int j = 0; j < 100; ++j) {
map.insert(i * 100 + j, i * 100 + j);
}
});
}
for (auto& t : threads) {
t.join();
}
int value;
if (map.find(42, value)) {
std::cout << "Found value: " << value << std::endl;
}
std::cout << "Map size: " << map.size() << std::endl;
return 0;
}
4.6 folly::ConcurrentSkipList
Сигнатура (упрощённо):
template <class T, class Compare = std::less<T>>
class ConcurrentSkipList {
bool insert(const T& value);
bool contains(const T& value) const;
size_t erase(const T& value);
class Accessor; // Для итерации
};
Что делает: Потокобезопасная реализация skip-list (список с пропусками). Поддерживает вставку, удаление и поиск за O(log n) в среднем. Не использует блокировки для чтения и минимальные блокировки для записи.
Пример:
#include <folly/concurrency/ConcurrentSkipList.h>
#include <iostream>
int main() {
folly::ConcurrentSkipList<int> skiplist;
skiplist.insert(10);
skiplist.insert(5);
skiplist.insert(20);
skiplist.insert(15);
std::cout << "Contains 10: " << skiplist.contains(10) << std::endl;
std::cout << "Contains 7: " << skiplist.contains(7) & Также в библиотеке
Мониторинг приложений Kotlin: обзор инструментов и библиотек
Rate Limiting в C# (.NET): руководство по библиотеке System.Threading.RateLimiting
Redis клиент для Swift (iOS/macOS) – RediStack
Генерация PDF отчётов на Java с iText и Apache PDFBox
Отправка SMS на Swift (iOS/macOS): API библиотеки MessageUI