ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

folly::to 与 tryTo:Meta 开源 C++ 高性能类型转换库 folly/Conv.h 完全指南

folly::to 与 tryTo:Meta 开源 C++ 高性能类型转换库 folly/Conv.h 完全指南 folly::to 与 tryToMeta 开源 C 高性能类型转换库 folly/Conv.h 完全指南【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly导读folly/Conv.h是 FollyFacebook/Meta 开源的 C 库中提供跨类型转换能力的一站式头文件核心接口只有to、tryTo、toAppend三个名字却在保证正确性的前提下把性能做到极致文档与源码标注的典型场景下整型转字符串可比boost::lexical_cast快数倍。本文围绕 folly/docs/Conv.md 展开结合 folly/Conv.h、folly/Conv.cpp 与 folly/test/ConvTest.cpp 的源码与测试系统讲解 API 用法、整型/浮点/字符串互转规则、边界检查语义、非抛异常接口tryTo以及自定义类型的扩展机制读完即可在项目中安全、高效地完成各类类型转换。概览为什么需要 folly/Conv.h原文档开宗明义folly/Conv.h是“一站式”类型转换工具核心卖点有三API 极简只需要记住to与toAppend两个名字同一头文件还提供了tryTo、toDelim、toAppendFit等衍生接口。速度文档指出 Folly 的转换比同类 API 快得多某些场景甚至快一个数量级。正确性提供边界检查、溢出检测、浮点精度保全等机制避免隐式转换埋下隐患。从源码看folly/Conv.h 的头文件注释给出了官方自述提供toTargetType(source)以及字符串类类型std::string、fbstring、StringPiece之间互转的通用接口同时支持带范围检查的数值/枚举互转并且机制可扩展——用户自定义类型可以通过实现parseTo/toAppend接入folly::to。快速上手toAppend 与 to 的基本用法所有示例默认已包含folly/Conv.h并using namespace folly;。追加式转换 toAppendtoAppend把值格式化后追加到目标字符串末尾目标字符串的地址作为最后一个参数传入// 格式化并追加到字符串末尾 fbstring str; toAppend(2.5, str); CHECK_EQ(str, 2.5); // 支持多个参数字符串指针放在最后 toAppend( is , 2, point , 5, str); CHECK_EQ(str, 2.5 is 2 point 5); // 不一定要用 fbstring虽然 fbstring 在转换与日常使用中更快 std::string stdStr; toAppend(Pi is about , 22.0 / 7, stdStr);返回值式转换 totoTargetType(sourceValue)按值返回转换结果stdStr tostd::string(Variadic , arguments also accepted.); // tofbstring 在典型负载下比 tostd::string 快约 2.5 倍 str tofbstring(Variadic , arguments also accepted.);从源码看folly/Conv.hL928-L940中toSomeString(v1, v2, ...)正是以toAppend为后端实现的——先构造目标字符串再调用toAppendFit(vs..., result)追加所有参数。这与文档“to对全部类型都是基于toAppend实现的”这一表述完全一致。变参与分隔符变体头文件注释还补充了原文档之外的同族接口folly/Conv.h L42-L59// 用分隔符拼接 auto s1 toDelimstd::string(,, 123, 456, 789); // 123,456,789 // 追加到已有字符串 std::string str 123; toAppend(456, 789, str); // str 123456789 // toAppendFit预先计算所需空间并 reserve避免反复扩容 // 注意对同一字符串反复 toAppendFit 反而可能变慢每次 append 都会扩容一次整型到整型转换零开销与范围检查使用toTarget(value)在整型之间转换时行为分两种情况目标类型能容纳源类型的所有取值时直接隐式转换零额外开销short x; unsigned short y; auto a toint(x); // 零开销转换 auto b toint(y); // 零开销转换否则to会插入边界检查越界时抛出std::range_errorshort x; unsigned short y; long z; x 123; auto a tounsigned short(x); // 正常 x -1; a tounsigned short(x); // THROWS负数无法放入 unsigned short z 2000000000; auto b toint(z); // 正常 z 1000000000; b toint(z); // THROWS溢出 int 上界 auto b tounsigned int(z); // 正常int 放不下unsigned int 可以源码印证detail::convertTofolly/Conv.h L1184-L1204在目标类型值域小于源类型时通过编译期if constexpr检查上界ARITH_POSITIVE_OVERFLOW与下界ARITH_NEGATIVE_OVERFLOW返回Expected后由to通过thenOrThrow转成std::range_error抛出folly/test/ConvTest.cpp如 L270、L398-L405用EXPECT_THROW(..., std::range_error)验证了这些越界路径。数值与枚举转换的扩展语义folly/Conv.h L78-L89 说明toT(S)的数值/枚举重载还覆盖以下情况浮点转整型且值带有非零小数部分时抛异常整型转浮点会丢失精度时抛异常ARITH_LOSS_OF_PRECISION枚举转换按底层类型做范围检查但不校验该值是否是合法的枚举成员算术转 bool遵循 C 惯例任何非零值都为true不做范围检查L259-L284。源码中convertTo的浮点↔整型实现L1294-L1310会做“往返校验”转过去再转回来必须值相等否则返回ARITH_LOSS_OF_PRECISION避免静默精度丢失。任意类型转字符串文档明确to与toAppend支持同一组源类型to按定义就是用toAppend实现的。toAppend(value, str)格式化并追加toStringType(value)返回结果值当前支持的字符串目标类型为std::string与fbstring。两者都支持变参toAppend的变参列表最后一个参数必须是受支持字符串类型的地址无需指定模板参数。整型转字符串整型按十进制格式转换负数带-前缀auto a tofbstring(123); assert(a 123); a tofbstring(-456); assert(a -456);性能与实现文档指出该实现被激进优化每次转换两个数字借助定长查找表完成将long转为fbstring比boost::lexical_cast快 3.6 倍比配合栈上定长缓冲区的sprintf快 2.5 倍。源码中toAppend的整型重载folly/Conv.h L557-L616使用folly/lang/ToAscii.h的to_ascii_decimal例程写入栈上缓冲区且“小于 4 字节的整型会先提升到 32/64 位中间类型再转换”L609-L616。为什么转 fbstring 有额外优势≤64 位的整型含符号最多 20 个十进制位fbstring对不超过 23 个字符启用小字符串优化SSO因此整型转fbstring保证不触发堆分配带来显著的速度与内存局部性收益基准测试显示典型负载下约 2 倍提升。源码注释L549-L556同样强调了这一点fbstring在 23 字节以下无动态分配而任何数字的文本表示不超过 22 字节。char 转字符串的特殊处理char虽然本质是整型但多数场景下a期望得到a而不是96因此folly/Conv.h对char做了特殊处理直接追加该字符本身folly/Conv.h L402-L405 中toAppend(char value, Tgt* result)为*result value。注意signed char与unsigned char仍按整型处理。浮点转字符串folly/Conv.h使用 V8 的 double-conversion 例程兼顾准确与速度典型负载下tofbstring(doubleValue)比sprintf快 1.9 倍、比boost::lexical_cast快 5.5 倍也比tostd::string(doubleValue)快 1.3 倍。从当前仓库源码看浮点格式化最终由detail::formatDouble实现定义在 folly/Conv.cppL714-L715中基于fmt::format_to_n(buf, bufSize, {}, value)输出最短往返表示浮点的toAppend重载folly/Conv.h L655-L668还会把Infinity/-Infinity/NaN分别格式化为对应文本。此外浮点转字符串走专用重载L953-L961不做预 reserve避免为大多数并不需要 24/25 字节的浮点值白白引入一次malloc。const char* 转字符串为完整性folly/Conv.h支持const char*含字符串字面量“转换”自然就是字符串本身空指针按空字符串处理源码 L425-L435 注释说明与operator(std::ostream, const char*)行为一致auto s tofbstring(Hello, world); assert(s Hello, world);从字符串解析string 转任意类型folly/Conv.h提供三类解析例程1. 严格区间解析toType(const char* begin, const char* end)严格转换[begin, end)区间为Type不允许首尾空白定位是更宽容例程的高效后端。源码中对应to(const char* b, const char* e)folly/Conv.h L1132-L1140内部调用tryToTgt(b, e)→digits_toTgt(b, e)失败时经makeConversionError抛出。2. 整串解析toType(stringy)stringy可以是const char*、StringPiece、std::string或fbstring技术上要求能隐式转换为StringPiece。3. 带进度信息解析toType(stringPiece)传入StringPiece*尽可能多地解析出Type并修改该 StringPiece移除已被消费的字符fbstring s 1234 angels on a pin; StringPiece pc(s); auto x toint(pc); assert(x 1234); assert(pc angels on a pin);注意它“吃掉”了前导空格但没有吃掉尾随空格。源码通过parseTo(StringPiece src, Tgt out)folly/Conv.h L1149-L1157将未消费的剩余部分以StringPiece形式返回再配合toint(pc)使用。解析整型整型解析按十进制进行有符号类型允许可选的/-号无符号类型不允许。速度是亮点解析典型long值比sscanf快 6 倍。实现采用激进的循环展开与表辅助的 SIMD 风格代码布局避免整型除法慢与跨操作数据依赖不利于指令级并行 ILP。示例fbstring str 12345 ; assert(toint(str) 12345); str 12345six seven eight; StringPiece pc(str); assert(toint(pc) 12345); assert(str six seven eight);解析浮点浮点解析同样以 V8 double-conversion 例程为后端速度比sscanf快 3 倍、比parsedouble之类的内部例程快 1.7 倍更重要的是准确性——即便自己写出更快的例程也很可能在各种边界用例上出错文档强烈建议使用todouble。当前仓库中浮点解析走str_to_floating→str_to_floating_fast_float_from_charsfolly/Conv.cpp L364-L411基于fast_float::from_chars_advanced并开启chars_format::general | allow_leading_plus支持float/double的显式模板实例化folly/Conv.h L1004-L1019。NaN / Infinity / 解析失败的特殊语义原文档专门列出了todouble对特殊字符串的处理规则这些规则在 folly/test/ConvTest.cppL639-L663 附近均有EXPECT_TRUE(std::isnan(...))、EXPECT_THROW(...)测试佐证传入任意大小写的NaN返回NaNfbstring str nan; // NaN, NAN, 等 double d todouble(str); if (std::isnan(d)) { // 字符串是 double 值 NaN 的合法表示 }传入任意大小写的-NaN同样返回NaN注意符号被忽略。传入任意大小写的inf或infinity返回正无穷fbstring str inf; // Inf, INF, infinity, Infinity, 等 double d todouble(str); if (std::isinf(d)) { // 字符串是 Infinity 或 -Infinity 的合法表示 }传入任意大小写的-inf/-infinity返回负无穷符号检测方式fbstring str -inf; // 或 inf, -Infinity, Infinity, 等 double d todouble(str); if (d std::numeric_limitsdouble::infinity()) { // 字符串是 Infinity 的合法表示 } else if (d -std::numeric_limitsdouble::infinity()) { // 字符串是 -Infinity 的合法表示 }不可解析的字符串如not-a-double、1.1.1、、$500.00会抛出异常而不是返回 NaNfbstring str not-a-double; // 或 1.1.1, , $500.00, 等 double d; try { d todouble(str); } catch (const std::range_error ) { // 字符串无法解析 }空字符串属于不可解析值同样导致todouble抛异常。非抛异常接口tryTotryToT是toT的非抛异常变体返回ExpectedT, ConversionCode。可以把Expected理解成OptionalT的扩展——转换失败时它存储的是错误码而不是T。性能转换成功时tryToT与toT性能相近错误路径上tryToT比抛异常的toT快约三个数量级且完全避免了栈展开stack unwinding带来的锁竞争。因此处理不可信输入时优先用tryTo保护自己免受异常路径拖慢。用法示例auto t1 tryToint(str); if (t1.hasValue()) { use(t1.value()); }Expected还提供组合能力让上面的模式更简洁tryToint(str).then([](int i) { use(i); });源码中ConversionCode枚举folly/Conv.h L149-L167定义了完整的错误码集合包括SUCCESS、EMPTY_INPUT_STRING、NO_DIGITS、BOOL_OVERFLOW、NON_DIGIT_CHAR、INVALID_LEADING_CHAR、POSITIVE_OVERFLOW、NEGATIVE_OVERFLOW、STRING_TO_FLOAT_ERROR、NON_WHITESPACE_AFTER_END、ARITH_POSITIVE_OVERFLOW、ARITH_NEGATIVE_OVERFLOW、ARITH_LOSS_OF_PRECISION、SPLIT_ERROR、CUSTOM等。抛异常版本抛出的ConversionError继承自std::range_errorL169-L185因此catch (const std::range_error)可以同时捕获两类错误。扩展机制为自定义类型接入 folly::tofolly/Conv.h的可扩展性体现在任何类型都可以通过实现以下函数接入folly::to体系folly/Conv.h L93-L104、L1340-L1399。为支持参数依赖查找ADL实现应放在该类型所在的命名空间// 1. 允许从字符串解析到自定义类型 // in 为输入串成功时返回未消费的剩余部分并写入 out ExpectedStringPiece, ConversionCode parseTo(folly::StringPiece in, YourType out); // 2. 将错误码翻译为可抛出的异常对象 YourErrorType makeConversionError(YourErrorType in, StringPiece in); // 3. 允许从自定义类型格式化为字符串追加到 out template class String void toAppend(const YourType in, String* out); // 4.可选估算格式化所需空间供 toAppendFit() 预分配避免重分配 size_t estimateSpaceNeeded(const YourType in);内置类型已提供大量现成实现parseTo对数值类型folly/Conv.h L1150-L1157、枚举L1352-L1361按底层类型解析后做范围检查、StringPieceL1363-L1368、std::string/std::string_view/fbstringL1382-L1399均有重载toAppend则覆盖char、const char*、字符串类型、StringPiece、各类整型/浮点、枚举以及__int128L504-L547等。整型到字符串的路径统一经过栈缓冲 to_ascii_decimal避免逐位取模的除法开销。测试与验证folly/test/ConvTest.cpp 是 Conv 模块最直接的验证依据覆盖了整型互转的零开销路径与std::range_error越界抛出L270、L398-L405无符号类型对号拒绝的边界L405 附近浮点解析的NaN/Infinity/错误输入语义L628-L663 附近toDelim的分隔符拼接L488-L506与toAppendFit的预分配行为L1614。若需自行验证可参照 folly/CMakeLists.txt 中 Conv 相关目标的构建方式在仓库根目录按 README.md 的指引配置 CMake 构建后运行ConvTest测试目标。小结记忆要点格式化输出用toAppend/to解析输入用toT(...)/tryToT全部集中在 folly/Conv.h 一个头文件。性能要点整型转fbstring因小字符串优化零分配浮点使用 V8 double-conversion / fmt 最短往返格式化与fast_float解析整型解析/格式化规避除法并采用表辅助与循环展开。正确性要点数值互转带范围检查越界抛std::range_error、浮点↔整型做精度保全校验、char按字符而非数字处理、todouble对 NaN/Infinity 有明确定义、空串与非法输入抛异常。健壮性要点处理不可信输入时用tryToT拿到ExpectedT, ConversionCode错误路径比异常快约三个数量级且无锁竞争。扩展性要点为自定义类型实现parseTo/toAppend/可选estimateSpaceNeeded/makeConversionError即可无缝接入整套转换体系。【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进