使用 C++ 实现¶
本食谱部分介绍了使用 Arrow C++ 实现时需要了解的基本概念。
使用 Status 和 Result¶
C++ 库通常需要在抛出异常和返回错误代码之间做出选择。Arrow 选择返回 Status 和 Result 对象作为折衷方案。这使得函数何时可能失败变得清晰,并且比使用整数错误代码更容易使用。
务必始终检查返回的 Status 对象的值,以确保操作成功。但是,这很快就会变得很繁琐
std::function<arrow::Status()> test_fn = [] {
arrow::NullBuilder builder;
arrow::Status st = builder.Reserve(2);
// Tedious return value check
if (!st.ok()) {
return st;
}
st = builder.AppendNulls(-1);
// Tedious return value check
if (!st.ok()) {
return st;
}
rout << "Appended -1 null values?" << std::endl;
return arrow::Status::OK();
};
arrow::Status st = test_fn();
rout << st << std::endl;
Invalid: length must be positive
宏 ARROW_RETURN_NOT_OK
将为您处理一些样板代码。它将运行包含的表达式并检查生成的 Status
或 Result
对象。如果失败,它将返回失败。
std::function<arrow::Status()> test_fn = [] {
arrow::NullBuilder builder;
ARROW_RETURN_NOT_OK(builder.Reserve(2));
ARROW_RETURN_NOT_OK(builder.AppendNulls(-1));
rout << "Appended -1 null values?" << std::endl;
return arrow::Status::OK();
};
arrow::Status st = test_fn();
rout << st << std::endl;
Invalid: length must be positive
使用访问者模式¶
Arrow 类 arrow::DataType
、arrow::Scalar
和 arrow::Array
针对每个 Arrow 类型都有专门的子类。为了针对每个子类专门化逻辑,您可以使用访问者模式。Arrow 提供内联模板函数,允许您高效地调用访问者
生成随机数据¶
请参阅 为给定模式生成随机数据 中的示例。
跨 Arrow 类型泛化计算¶
当编写可以处理多个数组类型的函数时,数组访问者非常有用。但是,为每个类型单独实现访问者可能会过于冗长。幸运的是,Arrow 提供类型特征,允许您编写模板函数来处理类型的子集。下面的示例演示了一个表格求和函数,该函数可以通过利用 arrow::enable_if_number
来处理任何整数或浮点数数组,并且只有一个访问者实现。
1class TableSummation {
2 double partial = 0.0;
3 public:
4
5 arrow::Result<double> Compute(std::shared_ptr<arrow::RecordBatch> batch) {
6 for (std::shared_ptr<arrow::Array> array : batch->columns()) {
7 ARROW_RETURN_NOT_OK(arrow::VisitArrayInline(*array, this));
8 }
9 return partial;
10 }
11
12 // Default implementation
13 arrow::Status Visit(const arrow::Array& array) {
14 return arrow::Status::NotImplemented("Can not compute sum for array of type ",
15 array.type()->ToString());
16 }
17
18 template <typename ArrayType, typename T = typename ArrayType::TypeClass>
19 arrow::enable_if_number<T, arrow::Status> Visit(const ArrayType& array) {
20 for (std::optional<typename T::c_type> value : array) {
21 if (value.has_value()) {
22 partial += static_cast<double>(value.value());
23 }
24 }
25 return arrow::Status::OK();
26 }
27}; // TableSummation
std::shared_ptr<arrow::Schema> schema = arrow::schema({
arrow::field("a", arrow::int32()),
arrow::field("b", arrow::float64()),
});
int32_t num_rows = 3;
std::vector<std::shared_ptr<arrow::Array>> columns;
arrow::Int32Builder a_builder = arrow::Int32Builder();
std::vector<int32_t> a_vals = {1, 2, 3};
ARROW_RETURN_NOT_OK(a_builder.AppendValues(a_vals));
ARROW_ASSIGN_OR_RAISE(auto a_arr, a_builder.Finish());
columns.push_back(a_arr);
arrow::DoubleBuilder b_builder = arrow::DoubleBuilder();
std::vector<double> b_vals = {4.0, 5.0, 6.0};
ARROW_RETURN_NOT_OK(b_builder.AppendValues(b_vals));
ARROW_ASSIGN_OR_RAISE(auto b_arr, b_builder.Finish());
columns.push_back(b_arr);
auto batch = arrow::RecordBatch::Make(schema, num_rows, columns);
// Call
TableSummation summation;
ARROW_ASSIGN_OR_RAISE(auto total, summation.Compute(batch));
rout << "Total is " << total;
Total is 21