【问题标题】:Creating ArrayBuilders in a Loop在循环中创建 ArrayBuilder
【发布时间】:2021-12-30 15:50:09
【问题描述】:

有什么方法可以创建arrow::ArrayBuilder 对象的动态容器?这是一个例子

int main(int argc, char** argv) {
  std::size_t rowCount = 5;
  arrow::MemoryPool* pool = arrow::default_memory_pool();  
  std::vector<arrow::Int64Builder> builders;
  for (std::size_t i = 0; i < 2; i++) {
    arrow::Int64Builder tmp(pool);
    tmp.Reserve(rowCount);
    builders.push_back(tmp);
  }

  return 0;
}

这会产生error: variable ‘arrow::Int64Builder tmp’ has initializer but incomplete type

理想情况下,我正在尝试构建一个集合,该集合将容纳各种构建器,并根据我收到的逐行数据构建一个表。我的猜测是这不是构建者的预期用途,但我在 Arrow 文档中找不到任何明确的内容

【问题讨论】:

    标签: c++ apache-arrow


    【解决方案1】:

    您的包含是什么样的?该错误消息似乎表明您没有包含正确的文件。 arrow:Int64Builder 的完整定义在 arrow/array/builder_primitive.h 中,但您通常可以只包含 arrow/api.h 以获取所有内容。

    以下为我编译:

    #include <iostream>
    
    #include <arrow/api.h>
    
    arrow::Status Main() {
        std::size_t rowCount = 5;
        arrow::MemoryPool* pool = arrow::default_memory_pool();
        std::vector<arrow::Int64Builder> builders;
        for (std::size_t i = 0; i < 2; i++) {
          arrow::Int64Builder tmp(pool);
          ARROW_RETURN_NOT_OK(tmp.Reserve(rowCount));
          builders.push_back(std::move(tmp));
        }
      return arrow::Status::OK();
    }
    
    int main() {
      auto status = Main();
      if (!status.ok()) {
        std::cerr << "Err: " << status << std::endl;
        return 1;
      }
      return 0;
    }
    

    您的示例的一个小变化是构建器没有复制构造函数/无法复制。所以我不得不std::move把它放到向量中。

    此外,如果您想要一个包含许多不同类型构建器的单一集合,那么您可能需要std::vector&lt;std::unique_ptr&lt;arrow::ArrayBuilder&gt;&gt;,并且您需要在堆上构建您的构建器。

    您可能会遇到的一个挑战是构建器对于Append 方法都有不同的签名(例如,Int64BuilderAppend(long),但StringBuilderAppend(arrow::util::string_view))。因此,arrow::ArrayBuilder 并没有真正的 Append 方法(如果您碰巧已经将数据作为 Arrow C++ 标量,则有一些方法采用标量)。但是,当您需要追加时,您可以通过转换为适当的类型来克服这个问题。

    更新:

    如果您真的想避免强制转换并且您提前知道架构,您可以按照以下方式做一些事情......

    std::vector<std::function<arrow::Status(const Row&)>> append_funcs;
    std::vector<std::shared_ptr<arrow::ArrayBuilder>> builders;
    for (std::size_t i = 0; i < schema.fields().size(); i++) {
      const auto& field = schema.fields()[i];
      if (isInt32(field)) {
        auto int_builder = std::make_shared<Int32Builder>();
        append_funcs.push_back([int_builder] (const Row& row) ({
          int val = row.GetCell<int>(i);
          return int_builder->Append(val);
        });
        builders.push_back(std::move(int_builder));
      } else if {
        // Other types go here
      }
    }
    
    // Later
    for (const auto& row : rows) {
      for (const auto& append_func : append_funcs) {
        ARROW_RETURN_NOT_OK(append_func(row));
      }
    }
    

    注意:我编造了Row,因为我不知道您的数据最初是什么格式。我还编造了isInt32,因为我不记得如何检查我的头顶。

    这使用shared_ptr 而不是unique_ptr,因为您需要两份副本,一份在lambda 的捕获中,另一份在builders 数组中。

    【讨论】:

    • 关于选角的第二点。我正在预先创建模式,它已经告诉我每列需要的类型。您是否知道我可以使用 Arrow 中的任何工具将 Field 类型与其适当的构建器相关联?或者这是我只能通过铸造才能做到的事情?
    • 请注意,我也不能在上面的示例中使用 ArrayBuilder ,因为它会产生 error: variable type 'arrow::ArrayBuilder' is an abstract class 。可能与一个单独的 SO 问题接壤,但我想我会先在这里发表评论
    • std::unique_ptr&lt;arrow::ArrayBuilder&gt; not arrow::ArrayBuilder 在 c++ 中,您可以拥有指向抽象基的指针向量,但不能拥有抽象基向量。 shared_ptr(或任何其他指针类型)也可以。我将使用一些伪代码来更新答案,您可以采取一种方法来避免提前使用模式进行强制转换。
    • 这太棒了。一如既往的感谢!
    猜你喜欢
    • 2020-09-16
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-19
    • 2016-05-26
    • 2018-11-16
    相关资源
    最近更新 更多