@原創文章,轉載請注明: 轉載自 鏡中影的技術部落格
本文連結位址: 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基類。詳細類圖描述如下:

具體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