mercredi 3 août 2022

How to build a type generator class based on the input data type and container type(by template arguments)?

There exists two basic data types in my tiny demo program, represented by the below classes:

struct FloatDataTypeDescriptor {
  using dtype = float;
};
struct Uint8DataTypeDescriptor {
  using dtype = uint8_t;
  uint8_t zero_point_;
  float scale_;
};

Conceptually, the data type descriptor and data actual holder(might be std::array, std::unique_ptr, std::vector...) are tightly couple together, So i decided to use std::pair to represent the data chunk, like:

using ChunkTypeA = std::pair<FloatDataTypeDescriptor, std::vector<FloatDataTypeDescriptor::dtype>>;
using ChunkTypeB = std::pair<Uint8DataTypeDescriptor, std::vector<Uint8DataTypeDescriptor::dtype>>;
using ChunkTypeC = std::pair<FloatDataTypeDescriptor, std::unique_ptr<FloatDataTypeDescriptor::dtype[]>;
// ...

This can work though, but writing such template alias all over the place is a little bit of tedious. So i've thought of using partial specialization to create a "type generator", produce the needed std::pair<> type by provided templates argument.

// primary template
template <typename TypeDescriptor, template<typename, typename...> class Container>
struct PairedTypeGenerator;

// partial specialization for std::vector
template <typename TypeDescriptor>
struct PairedTypeGenerator<TypeDescriptor, std::vector<typename TypeDescriptor::dtype>> {
  using type = std::pair<TypeDescriptor, std::vector<typename TypeDescriptor::dtype>>;
};

And use it like:

using a = PairedTypeGenerator<Uint8TypeDescriptor, std::vector>::type;

I've tried to use variadic template pack in the template template parameter Container. Since some Container might need extra argument other than the data type(like vector Allocator / unique_ptr Deleter). It didn't work, clang tolds me:

<source>:21:53: error: template argument for template template parameter must be a class template or type alias template
struct PairedTypeGenerator<TypeDescriptor, std::vector<typename TypeDescriptor::dtype>> {

Thanks to @463035818_is_not_a_number liberal and great advice, i continue to add more specialization for std::vector / std::unique_ptr / std::array

template <typename TypeDescriptor>
struct PairedTypeGenerator<TypeDescriptor, std::vector> {
  using type = std::pair<TypeDescriptor, std::vector<typename TypeDescriptor::dtype>>;
};

template <typename TypeDescriptor>
struct PairedTypeGenerator<TypeDescriptor, std::unique_ptr> {
  using type = std::pair<TypeDescriptor, std::unique_ptr<typename TypeDescriptor::dtype[]>>;
};


template <typename TypeDescriptor, typename Deleter>
struct PairedTypeGenerator<TypeDescriptor, std::unique_ptr, Deleter> {
  using type = std::pair<TypeDescriptor, std::unique_ptr<typename TypeDescriptor::dtype[], Deleter>>;
};

So now i can support case that std::unique_ptr with custom Deleter, just use it as:

using Ptr = EmbeddingPairedTypeGenerator<Uint8EmbeddingDataTypeDescriptor, std::unique_ptr, decltype(&std::free)>::type;

However, for std::array, things become much tricker, the std::array Container type need a non type template parameter, which cannot be matched by the parameter pack. I just want to use it by some syntax similar as:

using Array = PairedTypeGenerator<Uint8DataTypeDescriptor, std::array, 512>;

Aucun commentaire:

Enregistrer un commentaire