这篇博文旨在介绍 Facebook Folly 项目及其复杂的依赖关系链以及其用法。它还将展示 Conan 作为其安装及其依赖项的解决方案。此示例的源代码可以在 Github 上找到。

什么是 Folly

FollyFacebook 开源库 的“首字母缩写”,一个用社区协作开发的 C++ 项目,在 Github 上颇受欢迎,拥有超过 11,000 星和 2,000 次分支。该项目于 2012 年通过 Facebook 推出,目标是打造一个完整的库,专注于易用性、开发速度以及作为现实世界解决方案的补充,包括

  • 异步计算
  • 字符串格式化
  • 基准测试
  • 动态类型

Folly 也通过 CppCon 版本向世界推出,例如在 CppCon 2017 上的 “Facebook C++ 库的经验”,并在 Github 上有广泛的文档。

为什么我的项目应该使用 Folly?

我们已经有了 stdBoost,那么我们为什么还需要另一个核心库呢?事实上,Folly 的目标不是取代任何库,而是补充它们。Folly 是为那些需要更高性能或尚未实现的情况而设计的。目前,Facebook 本身在其 数百万台服务器 上使用它,这些服务器支持超过 20 亿用户,并且还用于 Facebook 移动应用,根据 Google Play 的数据,这些应用在超过 10 亿台设备上使用。这证明了 Folly 项目的成熟度和可靠性。

在 CppCon 2016 版本中,演示文稿 “Facebook 中 std::string 的奇怪细节” 展示了 Andrei Alexandrescu 在实现 FBString 方面所做的工作,这是一个旨在提高效率、与 std::string 兼容的类,其速度比 string::find() **快 30 倍**。

除了旨在实现高效率之外,Folly 还旨在易于使用,以加快新用户的集成和学习。例如,可以通过 Conv 简化字符串转换,或通过 Synchronized 简化互斥体同步,甚至可以使用 ProducerConsumerQueue 来同步多线程编程的队列。

光说不练假把式

为了说明 Folly 的用法,让我们用一个示例项目来验证一个 URI,使用 FuturesFBStringExecutorsFormat

#include <utility>
#include <iostream>
#include <folly/Format.h>
#include <folly/futures/Future.h>
#include <folly/executors/ThreadedExecutor.h>
#include <folly/Uri.h>
#include <folly/FBString.h>

static void print_uri(const folly::fbstring& address) {
    const folly::Uri uri(address);
    const auto authority = folly::format("The authority from {} is {}", uri.fbstr(), uri.authority());
    std::cout << authority << std::endl;
}

int main() {
    folly::ThreadedExecutor executor;
    folly::Promise<folly::fbstring> promise;
    folly::Future<folly::fbstring> future = promise.getSemiFuture().via(&executor);
    folly::Future<folly::Unit> unit = std::move(future).thenValue(print_uri);
    promise.setValue("https://conan.org.cn/");
    std::move(unit).get();
    return 0;
}

上面的代码应该在 future 回调执行后不久打印消息 “https://conan.org.cn 的 authority 为 conan.io”。让我们详细了解一下

static void print_uri(const folly::fbstring& address) {
    const folly::Uri uri(address);
    const auto authority = folly::format("The authority from {} is {}", uri.fbstr(), uri.authority());
    std::cout << authority << std::endl;
}

这个小函数负责解析地址、验证地址是否为有效的 URI、格式化 URI 上存在的 authority 字符串,并通过标准输出显示。 FBString 类有 3 种存储策略

  • 小字符串(<= 23 个字符)在原地存储,无需内存分配。
  • 中等字符串(24 - 255 个字符)存储在 malloc 分配的内存中并急切复制。
  • 大字符串(> 255 个字符)存储在 malloc 分配的内存中并延迟复制。

接收到的地址只有 17 个字符,因此它将存储在没有内存分配的情况下。 Uri 将在其构造函数中解析地址,为此将使用 Boost Regex。最后将使用快速而强大的 folly::format 进行格式化。

folly::Future<folly::fbstring> future = promise.getSemiFuture().via(&executor);
folly::Future<folly::Unit> unit = std::move(future).thenValue(print_uri);
promise.setValue("https://conan.org.cn/");
std::move(unit).get();

Future 不过是对异步计算结果的一种表示,该结果可能尚未可用。完成后,它将包含执行的操作的结果。Folly Future 能够通过 thenValuethenTry 在完成之后执行回调。执行器指定工作将在哪里运行,因此,给定一个执行器,我们可以将 SemiFuture 转换为带有执行器的 Future。最后,我们将要由回调函数使用值设置为并通过 get() 等待结果。

现在我们有了示例代码和构建描述,我们需要一个环境来构建项目,包括 Folly 库及其依赖项。

Folly 的依赖项

虽然 Folly 是 C++ 环境的一个优秀项目,但它具有复杂的依赖关系结构

Folly's dependency graph

此图可以使用 conan info 命令生成

$ conan info folly/2018.11.12.00@bincrafters/stable --graph index.html

如所述,Conan 列出了另外 11 个与 Folly 直接相关的项目,包括 Boost 库。现在您可以意识到准备使用 Folly 的环境的任务有多么困难和耗时。

该项目本身列出了如何在 Linux、Windows 和 OSX 平台上解决其依赖项。但是,在 Linux 上,必须使用发行版提供的版本,并且不总是想要的版本。在 Windows 上,仍然可以选择使用 Vcpkg,但是,您需要等待所有依赖项构建完成后才能编译任何示例代码。

Conan 来救援

Conan C++ 包管理器能够计算依赖关系图并解决依赖关系。为了解决此类依赖关系,必须创建一个小文件 conanfile.txt 来告诉 conan 获取 conan Folly 包并为我们方便起见生成一个 cmake 文件

[requires]
folly/2018.11.12.00@bincrafters/stable

[generators]
cmake

该文件显示了我们项目的要求,在本例中仅为 Folly 项目,由 Bincrafters 分发,版本为 2018.11.12.00,可在 BintrayConan 中心 上获得,后者在 conan-cli 中预先配置。由于我们使用 CMake 构建项目,因此我们需要告诉 Conan 运行其 cmake 生成器 以提供一个文件,其中包含所有必要的设置,以便能够导入 Folly。虽然我们在此项目中使用 cmake,但 Conan 还有其他几个 生成器,包括 QMakeVisual StudioXCode 等。

Folly 列出的所有 11 个依赖项都将由 Conan 自动解决!

不幸的是,仅靠 CMake 无法解决 Folly 需要的所有额外依赖项,因此我们需要一起使用 Conan 和 CMake 来声明额外的包。让我们编写 CMakeLists.txt

cmake_minimum_required(VERSION 3.1.3)
project(folly_example CXX)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)

add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} CONAN_PKG::folly)
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 14)

函数 conan_basic_setup 将负责配置所有必要的设置,包括头文件和库文件的路径。 conanbuildinfo.cmake 由 Conan 生成,一旦配置的生成器类型为 cmake。Conan 生成的 CONAN_PKG::folly 目标拥有 Folly 项目,以及所有列为依赖项的库。

构建时间

现在 CMake 文件已更新,Conan 脚本已列出相应的依赖项,我们可以构建我们的示例

$ mkdir build && cd build
$ conan install ..
$ cmake .. -G "Visual Studio 15 Win64"
$ cmake --build . --config Release

conan install 命令负责读取 conanfile.txt 文件,根据默认配置文件(基于主机配置)下载和安装 Folly,并生成包含我们下一步所需的所有信息的 conanbuildinfo.cmake。使用 CMake 的命令将负责生成构建文件,以及构建示例。

构建完成后,我们可以运行我们的示例项目

$ bin/folly_example
 Callback Future: Hello World!

结论

C++ 世界拥有像 Folly 这样令人难以置信的项目来帮助解决现实世界的问题,但是,准备一个包含所有必要依赖项的环境可能是一项漫长而痛苦的任务。虽然 Folly 是您项目的优秀工具,但与 Boost 正则表达式等相关的 11 个项目的复杂性可以被视为避免使用它的不利因素。Folly 项目的案例证明了依赖项管理器和 Conan 等包对于现代 C++ 开发环境的重要性。

您是否有兴趣发表评论、提问或建议?请打开一个 问题