天天看點

Apache Arrow源碼分析(二)——類型的封裝引言DataType類圖結構和詳細分析參考資料

@原創文章,轉載請注明: 轉載自 鏡中影的技術部落格

本文連結位址: Apache Arrow源碼分析(二)——類型的封裝

URL:http://blog.csdn.net/linkpark1904/article/details/51000719

引言

對于向量容器而言,需要适配底層多種資料類型。C++在語言層面提供了諸如int,double,float,char等基礎類型,但是對于程式庫而言,需要自己定義多種類型的資料結構,當然是建構在C++語言層面提供的基礎資料類型之上,就需要對資料類型進行封裝。Arrow項目中亦有涉及對資料類型(DataType)的封裝,采用C++11實作,很多新特性值得我們學習和借鑒,這裡單獨拿出來分析。

DataType類圖結構和詳細分析

DataType的定義從struct type開始,struct type中包含enum 變量type用來描述arrow支援的邏輯類型,抽象出類DataType來表示類型基類,所有自定義類型都繼承自DataType基類。詳細類圖描述如下:

Apache Arrow源碼分析(二)——類型的封裝引言DataType類圖結構和詳細分析參考資料

具體Type類的代碼如下所示(Type.h):

struct Type {
  enum type {
    // A degenerate NULL type represented as 0 bytes/bits
    NA = 0,

    // A boolean value represented as 1 bit
    BOOL = 1,

    // Little-endian integer types
    UINT8 = 2,
    INT8 = 3,
    UINT16 = 4,
    INT16 = 5,
    UINT32 = 6,
    INT32 = 7,
    UINT64 = 8,
    INT64 = 9,
    ....
    // Exact timestamp encoded with int64 since UNIX epoch
    // Default unit millisecond
    TIMESTAMP = 17,

    // Timestamp as double seconds since the UNIX epoch
    TIMESTAMP_DOUBLE = 18,

    // Exact time encoded with int64, default unit millisecond
    TIME = 19,

    // Precision- and scale-based decimal type. Storage type depends on the
    // parameters.
    DECIMAL = 20,

    // Decimal value encoded as a text string
    DECIMAL_TEXT = 21,
    ...
    }
};
           

其中隻包含了一組枚舉類型,對于DataType類的實作代碼如下所示:

struct DataType {
  Type::type type;

  std::vector<std::shared_ptr<Field>> children_;

  explicit DataType(Type::type type) :
      type(type) {}

  virtual ~DataType();

  bool Equals(const DataType* other) {
    // Call with a pointer so more friendly to subclasses
    return this == other || (this->type == other->type);
  }

  bool Equals(const std::shared_ptr<DataType>& other) {
    return Equals(other.get());
  }

  const std::shared_ptr<Field>& child(int i) const {
    return children_[i];
  }

  int num_children() const {
    return children_.size();
  }

  virtual int value_size() const {
    return -;
  }

  virtual std::string ToString() const = ;
};

typedef std::shared_ptr<DataType> TypePtr;
           

基類中定義了一系列接口和方法,包括類型的判等,類型長度等相關方法,其中關于成員變量

std::vector<std::shared_ptr<Field>> children_

代碼注釋裡給出的解釋是

A field is a piece of metadata that includes (for now) a name and a data type

。 另外,關于DataType類的子類,有四個PrimitiveType,StringType,ListType,StructType,這裡PrimitiveType抽象為系統本身提供的變量類型例如int32_t,double,float等,PrimitiveType具體實作代碼如下:

template <typename Derived>
struct PrimitiveType : public DataType {
  PrimitiveType() : DataType(Derived::type_enum) {}

  std::string ToString() const override;
};

template <typename Derived>
inline std::string PrimitiveType<Derived>::ToString() const {
  std::string result(static_cast<const Derived*>(this)->name());
  return result;
}
           

這段代碼中出現了一個C++11引入的關鍵字override,顯示的告訴編譯器,子類PrimitiveType需要重寫基類DataType的ToString方法。對于PrimitiveType子類NullType,BooleanType等的實作,Arrow代碼中的做法值得借鑒,觀察可知,PrimitiveType子類很多功能代碼都是重複的,為了避免重複,Arrow利用宏定義來定義公共部分代碼,具體實作如下:

#define PRIMITIVE_DECL(TYPENAME, C_TYPE, ENUM, SIZE, NAME)  \
  typedef C_TYPE c_type;                                    \
  static constexpr Type::type type_enum = Type::ENUM;       \
                                                            \
  TYPENAME()                                                \
      : PrimitiveType<TYPENAME>() {}                        \
                                                            \
  virtual int value_size() const {                          \
    return SIZE;                                            \
  }                                                         \
                                                            \
  static const char* name() {                               \
    return NAME;                                            \
  }

struct NullType : public PrimitiveType<NullType> {
  PRIMITIVE_DECL(NullType, void, NA, , "null");
};

struct BooleanType : public PrimitiveType<BooleanType> {
  PRIMITIVE_DECL(BooleanType, uint8_t, BOOL, , "bool");
};

struct UInt8Type : public PrimitiveType<UInt8Type> {
  PRIMITIVE_DECL(UInt8Type, uint8_t, UINT8, , "uint8");
};
           

具體說明,上述代碼中BooleanType通過宏定義展開實際上是這樣的:

struct BooleanType : public PrimitiveType<BooleanType> {
  typedef uint8_t c_type;                                    
  static constexpr Type::type type_enum = Type::BOOL;       

  BooleanType()                                                
      : PrimitiveType<BooleanType>() {}                        

  virtual int value_size() const {                         
    return ;                                            
  }                                                         

  static const char* name() {                               
    return "bool";                                            
  }
};
           

這種寫法,能夠用三行代碼來定義一個類,确實比較簡潔。另外這裡出現了C++11的又一個關鍵字,constexpr 是常量表達式關鍵字,其作用在于告訴編譯器該類型變量是常量,于是在編譯期就可以确定該變量的值。這裡Type::type是枚舉類型,是以在編譯期就可以确定其值(表示一種具體的類型)。

對于非系統類型變量的封裝,arrow抽出了兩種類型,ListType和StringType

struct ListType : public DataType {
  // List can contain any other logical value type
  explicit ListType(const std::shared_ptr<DataType>& value_type)
      : DataType(Type::LIST) {
    children = {std::make_shared<Field>("item", value_type)};
  }

  explicit ListType(const std::shared_ptr<Field>& value_field)
      : DataType(Type::LIST) {
    children = {value_field};
  }

  const std::shared_ptr<Field>& value_field() const {
    return children[];
  }

  const std::shared_ptr<DataType>& value_type() const {
    return children[]->type;
  }

  static char const *name() {
    return "list";
  }

  std::string ToString() const override;
};
           

這裡,官方對ListType定義為

List can contain any other logical value type

于是children_成員變量就有了用武之地,用來表示連結清單中存儲的元素(item)的類型,對于ListType的測試代碼如下所示(List-test.cc):

TEST(TypesTest, TestListType) {
  std::shared_ptr<DataType> vt = std::make_shared<UInt8Type>();

  ListType list_type(vt);
  ASSERT_EQ(list_type.type, Type::LIST);

  ASSERT_EQ(list_type.name(), string("list"));
  ASSERT_EQ(list_type.ToString(), string("list<item: uint8>"));

  ASSERT_EQ(list_type.value_type()->type, vt->type);
  ASSERT_EQ(list_type.value_type()->type, vt->type);

  std::shared_ptr<DataType> st = std::make_shared<StringType>();
  std::shared_ptr<DataType> lt = std::make_shared<ListType>(st);
  ASSERT_EQ(lt->ToString(), string("list<item: string>"));

  ListType lt2(lt);
  ASSERT_EQ(lt2.ToString(), string("list<item: list<item: string>>"));
}
           

可以看出,該List的item類型為uint8。另一方面對于StringType和StructType的實作,Arrow隻是單純的繼承DataType,沒有做其他特殊的操作,就不再贅述了。

參考資料

深入了解C++11

繼續閱讀