资讯动态

Apache Arrow C++ Scalar API 完全指南:从基类、工厂函数到全部具体子类

发布时间:2026/9/14 22:04:53 来源:尧图企业网站定制
Apache Arrow C Scalar API 完全指南从基类、工厂函数到全部具体子类【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow本文是一份面向 Apache Arrow C 开发者的 Scalar API 实战指南完整覆盖 docs/source/cpp/api/scalar.rst 所定义的 API 面基类arrow::Scalar、标量工厂函数、数十个具体标量子类以及ScalarVisitor访问器工具。Scalar 在 Arrow 中用于表示单个值 类型的对象模型是向计算函数compute传递单值输入、表示数组单个元素的基础设施。读完本文你将掌握如何创建、校验、比较、转换和遍历各类 Arrow 标量并能理解其与Array、DataType、Datum之间的关系。一、Scalar 是什么定位与设计意图在 cpp/src/arrow/scalar.h 的文件头注释中Arrow 明确说明了 Scalar 的定位Object model for scalar (non-Array) values. Not intended for use with large amounts of data.即Scalar 是非数组的单值对象模型不适用于承载大量数据——每个标量是一个独立堆对象用它们逐个包装数组元素会有显著的non-trivial包装开销。它是为以下场景设计的向 compute 函数传递单值输入例如add、take、filter中的标量参数、字面量表达式表示数组中的单个元素代价较高因此只用于少量场景作为嵌套结构如 Struct、List、Union内部节点的组成部分。arrow::Scalar的继承体系位于arrow命名空间并使用了ARROW_EXPORT导出宏表明它是 Arrow C 公共 API 的一部分。基类同时继承自std::enable_shared_from_thisScalar与util::EqualityComparableScalar——前者支持从const Scalar上下文安全地获取shared_ptr后者为标量提供基于Equals()的相等性比较语义。Scalar 与计算内核的衔接点在于DatumDatum可以持有 Scalar、Array 等不同形态的数据compute 内核通过类型分发决定按标量还是按数组处理。这也是 Scalar 与 cpp/src/arrow/datum.h 产生关联的入口。二、基类arrow::Scalar成员与核心方法Scalar是一个struct公开了以下核心成员源码见 cpp/src/arrow/scalar.h 第 54 行起的定义2.1 数据成员成员类型含义typestd::shared_ptrDataType该标量值的类型Int32、String、List 等is_validbool值是否有效非 null。默认false即构造出的标量默认视为 null需要注意的是is_valid对于所有类型并不总是反映逻辑空值。以字典标量DictionaryScalar为例is_valid只反映索引的有效性——一个有效索引仍可能指向字典中的 null 值此时该标量在逻辑上是 null 的。因此基类提供了virtual bool IsLogicalNull() const { return !is_valid; }该虚函数可被子类重写DictionaryScalar就重写了它。与数组层面的逻辑空值计算一致IsLogicalNull()不会递归进入嵌套值被 union、run-end encoded 或 extension 标量包裹的字典值按其包裹层的is_valid报告。这与ArrayData::ComputeLogicalNullCount的语义保持一致参见 cpp/src/arrow/array/data.h 中的相关注释。2.2 比较与哈希bool Equals(const Scalar other, const EqualOptions options EqualOptions::Defaults()) const; bool ApproxEquals(const Scalar other, const EqualOptions options EqualOptions::Defaults()) const;Equals精确比较。实现位于 cpp/src/arrow/scalar.cc委托给ScalarEquals(*this, other, options)ApproxEquals近似比较浮点类型在容差范围内视为相等委托给ScalarApproxEqualshash()返回标量的哈希值配合内嵌的Hash仿函数使用可直接用于std::unordered_setstd::shared_ptrScalar等哈希容器struct ARROW_EXPORT Hash { size_t operator()(const Scalar scalar) const { return scalar.hash(); } size_t operator()(const std::shared_ptrScalar scalar) const { return scalar-hash(); } };EqualOptions定义于 cpp/src/arrow/compare.h可配置如浮点比较的atol绝对容差等选项。2.3 字符串表示与解析std::string ToString() const; static Resultstd::shared_ptrScalar Parse(const std::shared_ptrDataType type, std::string_view repr);ToString()生成人类可读的标量字符串表示Parse(type, repr)从字符串表示解析出指定类型的标量如Parse(int32(), 42)解析失败返回Status错误。2.4 校验Status Validate() const; // 轻量校验O(k)k 为后代节点数 Status ValidateFull() const; // 深度校验最坏 O(k*n)n 为后代长度涉及 list 标量时Validate()执行廉价校验复杂度为O(k)其中k是后代节点数ValidateFull()执行全面数据校验当涉及 list 标量时可能达到O(k*n)。对于格式错误的标量Validate()即可发现但越界的字典索引只有ValidateFull()能检测到见IsLogicalNull()的注释。2.5 类型转换与访问器分发Resultstd::shared_ptrScalar CastTo(std::shared_ptrDataType to) const; Status Accept(ScalarVisitor* visitor) const; std::shared_ptrScalar GetSharedPtr() const; // EXPERIMENTALCastTo将标量转换为目标类型返回Result失败时携带错误状态Accept按标量实际类型分派到ScalarVisitor对应的Visit()重载。实现在 cpp/src/arrow/scalar.cc 中Status Scalar::Accept(ScalarVisitor* visitor) const { return VisitScalarInline(*this, visitor); }它通过visit_scalar_inline.h中的内联类型分发机制把this的运行时类型映射到visitor的编译期重载——这是 Arrow 中典型的访问者模式 内联分发组合GetSharedPtr标记为 EXPERIMENTAL用于在只有const Scalar引用的上下文中获得shared_ptrScalar。此外PrintTo(const Scalar scalar, std::ostream* os)全局函数支持将标量输出到任意std::ostream配合 GoogleTest 的EXPECT_EQ失败信息打印使用。三、标量工厂函数Scalar factory functionsscalar-factories组定义于 cpp/src/arrow/scalar.h 第 948 行起提供了三种主要工厂3.1MakeNullScalar——空标量ARROW_EXPORT std::shared_ptrScalar MakeNullScalar(std::shared_ptrDataType type);为任意类型构造一个 null 标量is_valid false。例如MakeNullScalar(int64())返回一个 null 的Int64Scalar。3.2MakeScalar(type, value)——带类型的标量template typename Value Resultstd::shared_ptrScalar MakeScalar(std::shared_ptrDataType type, Value value);根据传入的DataType与 C 值构造非空标量返回Result。其底层由MakeScalarImpl实现通过VisitTypeInline(*type_, this)对类型做内联分发见 cpp/src/arrow/scalar.h 第 985 行起的MakeScalarImpl对一般原始类型直接std::make_sharedScalarType(...)对HalfFloatType有专门的重载因为util::Float16不能隐式转换为uint16_t对ExtensionType先对存储类型递归调用MakeScalar再包一层ExtensionScalar对std::string输入且目标为 binary/string 类类型时通过Buffer::FromString构造Buffer再包装遇到不支持从裸值构造的类型时返回Status::NotImplemented(constructing scalars of type ... from unboxed values)。3.3MakeScalar(value)——类型推断工厂template typename Value, typename Traits CTypeTraitstypename std::decayValue::type, ... std::shared_ptrScalar MakeScalar(Value value); inline std::shared_ptrScalar MakeScalar(std::string value); inline std::shared_ptrScalar MakeScalar(const std::shared_ptrScalar scalar);该重载根据输入 C 类型的type_singleton()自动推断DataType例如int8_t输入得到Int8Scalar。文档注释明确指出其限制Only non-parametric primitive types and String are supported.即只支持无参数原始类型数值、布尔等与std::stringstd::string输入生成StringScalarshared_ptrScalar输入则原样返回。典型用法#include arrow/scalar.h #include arrow/type.h using namespace arrow; // 类型推断工厂 auto s1 MakeScalar(42); // Int32Scalar (value42) auto s2 MakeScalar(std::string(hi)); // StringScalar // 显式类型工厂 auto s3 MakeScalar(float64(), 3.14); // DoubleScalar auto s4 MakeNullScalar(utf8()); // null StringScalar // 比较与字符串化 bool eq s3-Equals(*MakeScalar(float64(), 3.14)); // true std::string repr s3-ToString();四、具体标量子类族Concrete Scalar subclassesconcrete-scalar-classes组覆盖了 Arrow 全部类型体系对应的标量类。理解它们的关键是掌握 Arrow 标量类与类型类TypeClass、值类型ValueType的对应关系——每个具体标量类都通过using TypeClass ...声明它对应的DataType子类通过ValueType声明 C 侧的存储类型。4.1 Null 与布尔NullScalar对应NullType永远无效构造函数硬编码is_valid false。也就是说null 标量只有一个无法表示有效的 null 值BooleanScalar继承internal::PrimitiveScalarBooleanType, bool值类型为bool支持BooleanScalar(bool value)构造。4.2 数值标量NumericScalarT模板继承internal::PrimitiveScalarT公开了ValueType value成员、data()返回值的内存指针与view()返回值的字节视图std::string_view。具体类包括标量类类型类C 值类型Int8Scalar/Int16Scalar/Int32Scalar/Int64Scalar对应整数类型int8_t/int16_t/int32_t/int64_tUInt8Scalar…UInt64Scalar对应无符号整数类型uint8_t…uint64_tHalfFloatScalarHalfFloatType以uint16_t位模式存储另有接收util::Float16的便捷构造FloatScalar/DoubleScalarFloatType/DoubleTypefloat/double注意HalfFloatScalar由于util::Float16不能隐式转换为uint16_t它提供了HalfFloatScalar(util::Float16 value)的专门构造源码见 cpp/src/arrow/scalar.h 第 266 行。4.3 二进制与字符串标量BaseBinaryScalar是所有二进制/字符串标量的公共基类ValueType为std::shared_ptrBuffer即值以Buffer承载data()与view()分别返回缓冲区的指针和string_view视图。文档注释特别强调值在构造后不应被修改因为子类内部有一块 scratch space暂存区其内容必须与value保持一致。标量类类型类BinaryScalar/StringScalarBinaryType/StringTypeBinaryViewScalar/StringViewScalarBinaryViewType/StringViewTypeLargeBinaryScalar/LargeStringScalarLargeBinaryType/LargeStringTypeFixedSizeBinaryScalarFixedSizeBinaryType这些类大多支持三种构造(std::shared_ptrBuffer, type)、(std::string, type)内部转成Buffer、以及默认/单参数便捷构造如StringScalar(std::string s)自动使用utf8()类型。BinaryScalar等类还私有继承internal::ArraySpanFillFromScalarScratchSpace它提供 16 字节sizeof(int64_t) * 2的 scratch space用于把二进制标量转成ArraySpan视图时伪装出两个 32 位或 64 位 offset——这是标量到数组跨度视图互转的关键机制见 cpp/src/arrow/scalar.h 第 154 行起的kScalarScratchSpaceSize定义。FixedSizeBinaryScalar额外带bool is_valid true参数并校验缓冲区长度internal::CheckBufferLength实现在 cpp/src/arrow/scalar.cc。4.4 时间与区间标量TemporalScalarT系列覆盖 Arrow 的日期时间类型值类型为其底层整数计数标量类类型类说明Date32Scalar/Date64ScalarDate32Type/Date64Type自 epoch 起的天数 / 毫秒数Time32Scalar/Time64ScalarTime32Type/Time64Type支持(value, TimeUnit::type unit)构造TimestampScalarTimestampType支持(value, unit, tz)构造静态方法FromISO8601(iso8601, unit)从 ISO 8601 字符串解析DurationScalarDurationType提供从std::chrono::nanoseconds/microseconds/milliseconds/seconds便捷构造的重载模板见 cpp/src/arrow/scalar.h 第 549 行起MonthIntervalScalar/DayTimeIntervalScalar/MonthDayNanoIntervalScalar对应 Interval 类型DayTimeInterval底层为DayMilliseconds结构4.5 十进制标量DecimalScalarTYPE_CLASS, VALUE_TYPE模板value类型为Decimal32/Decimal64/Decimal128/Decimal256来自 cpp/src/arrow/util/decimal.h派生四个具体类Decimal32Scalar、Decimal64Scalar、Decimal128Scalar、Decimal256Scalar。其data()返回value.native_endian_bytes()十进制值的本机字节序表示view()返回定长字节视图宽度为ValueType::kByteWidth。4.6 嵌套类型标量嵌套标量的ValueType是std::shared_ptrArrayList 系或标量向量Struct/Union体现了标量可以递归包含数组/标量的树形结构BaseListScalar家族ListScalar、LargeListScalar、ListViewScalar、LargeListViewScalar、MapScalar、FixedSizeListScalar。前五个私有继承 scratch space 机制value为std::shared_ptrArrayStructScalarvalue为ScalarVectorstd::vectorstd::shared_ptrScalar提供Resultstd::shared_ptrScalar field(FieldRef ref)按字段名/索引取值以及静态工厂Make(value, field_names)SparseUnionScalar即使只有一个 union 成员相关仍构造每个 union 值一个标量的向量以支持从该标量重建长度为 1 的合法ArraySpanchild_id指出当前激活的成员FromValue静态方法可从单个标量便捷构造DenseUnionScalarvalue为单个std::shared_ptrScalarchild_value()直接返回它RunEndEncodedScalar包裹一个逻辑值标量run_end_type()/value_type()分别暴露 REE 类型的 run-end 与值类型支持从 null 类型构造 null 标量DictionaryScalarValueType为{index, dictionary}结构索引标量 字典数组。Make(index, dict)静态构造GetEncodedValue()返回解引用字典后的实际值重写了IsLogicalNull()——is_valid只反映索引有效性逻辑空值还需检查字典中被引用值见 cpp/src/arrow/scalar.h 第 879 行的类注释ExtensionScalarvalue为存储类型标量构造约束为is_valid为 true 仅当value非空且value-is_valid为 true。五、ScalarVisitor标量类型分发的访问器Utilities一节指向arrow::ScalarVisitor定义于 cpp/src/arrow/visitor.h 第 140 行起/// \brief Abstract scalar visitor class /// /// Subclass this to create a visitor that can be used with the Scalar::Accept() /// method. class ARROW_EXPORT ScalarVisitor { public: virtual ~ScalarVisitor() default; virtual Status Visit(const NullScalar scalar); virtual Status Visit(const BooleanScalar scalar); virtual Status Visit(const Int8Scalar scalar); // ... 每个具体标量类一个 Visit 重载 ... virtual Status Visit(const ExtensionScalar scalar); };用法模式class MyScalarVisitor : public arrow::ScalarVisitor { arrow::Status Visit(const arrow::Int64Scalar s) override { // 处理 Int64 标量 return arrow::Status::OK(); } arrow::Status Visit(const arrow::StringScalar s) override { // 处理 String 标量 return arrow::Status::OK(); } // 其余重载可选择性覆盖基类默认实现返回 NotImplemented 或 OK }; arrow::Scalar* sc ...; ARROW_RETURN_NOT_OK(sc-Accept(visitor));ScalarVisitor与ArrayVisitor、TypeVisitor共同构成 Arrow 的三套访问器。使用时只需覆盖关心的Visit()重载未覆盖的类型会走基类默认实现返回NotImplemented源码可见 cpp/src/arrow/visitor.cc。这是实现按标量类型做多态处理的标准方式也是Accept→VisitScalarInline内联分发链路的最终落点。六、从源码到实践测试与调用链印证仓库中的测试 cpp/src/arrow/scalar_test.cc 是理解这些 API 行为的最佳活文档。测试覆盖了各类标量的构造、默认值null、is_valid语义Equals/ApproxEquals/hash的相等性与哈希一致性MakeScalar/MakeNullScalar工厂的类型推断与显式类型构造DictionaryScalar的IsLogicalNull与GetEncodedValue逻辑StructScalar的field()字段访问、嵌套标量的递归比较Validate/ValidateFull对畸形标量如越界字典索引的检测行为。关键调用链总结用户代码 → MakeScalar / MakeNullScalar (scalar.h 工厂函数) → MakeScalarImpl::Visit → VisitTypeInline (按 DataType 分派) → 具体 Scalar 子类构造 → Scalar::Equals/ApproxEquals (scalar.cc → ScalarEquals/ScalarApproxEquals) → Scalar::Accept(visitor) (scalar.cc → VisitScalarInline → ScalarVisitor::Visit) → compute 内核通过 Datum 接收标量输入在 compute 层如 cpp/src/arrow/compute/kernels 目录下的内核标量通常作为Datum的一种形态出现内核判断输入是数组还是标量后走标量执行路径或数组广播路径。这也是 Scalar API 最核心的生产用途。七、注意事项与最佳实践不要用 Scalar 承载大数据每个标量是独立堆对象逐元素包装数组开销显著批量场景应使用Array/ChunkedArray区分is_valid与IsLogicalNull()尤其是DictionaryScalar判断逻辑空值必须调用IsLogicalNull()嵌套标量的只读约定BaseBinaryScalar、BaseListScalar、UnionScalar等类的注释都强调值构造后不得修改因为子类的 scratch space 需要与值保持一致违反约定会导致ArraySpan视图错乱类型推断工厂的适用范围MakeScalar(value)只支持无参数原始类型与std::string需要显式控制类型如时间戳、十进制、嵌套类型时务必使用MakeScalar(type, value)双参形式校验分层常规场景用Validate()涉及字典索引越界等深层一致性检查时使用ValidateFull()类型分派优先用AcceptScalarVisitor避免手写dynamic_cast链Arrow 已为你准备好了完整的访问器协议。结语Scalar API 虽然对象模型简单一个类型 一个值却是 Arrow 多语言工具链中连接数组世界与单值计算世界的桥梁。本文从基类成员、工厂函数、全部具体子类到ScalarVisitor访问器完整还原了 docs/source/cpp/api/scalar.rst 定义的 API 面并结合 cpp/src/arrow/scalar.h、cpp/src/arrow/scalar.cc、cpp/src/arrow/visitor.h 与 cpp/src/arrow/scalar_test.cc 给出了源码级依据。后续在编写 compute 内核或表达式系统相关代码时可继续参考 docs/source/cpp/api/compute.rst 了解标量如何作为Datum参与计算管线。【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价