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>;
CodePudding user response:
std::vector<typename TypeDescriptor::dtype>
is not a template. Its a type. In the specialization you want to specialize for std::vector
not for a particular instantiation of it. The error is due to the primary template expecting a template as second parameter rather than a type.
#include <vector>
struct FloatDataTypeDescriptor {
using dtype = float;
};
struct Uint8TypeDescriptor {
using dtype = unsigned;
};
// 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> {
// ^^^------------------------
using type = std::pair<TypeDescriptor, std::vector<typename TypeDescriptor::dtype>>;
};
using a = PairedTypeGenerator<Uint8TypeDescriptor, std::vector>::type;
However, you only need a single alias template, no specializations:
template <typename T,template <typename,typename...> class Container>
using PairedType = std::pair<T,Container<typename T::dtype>>;
using a = PairedType<Uint8TypeDescriptor,std::vector>;
CodePudding user response:
It is possible if you're willing to accept helpers/wrappers.
Here's my take on this:
template <auto X>
struct Constant {
static constexpr auto value = X;
};
template <typename T, typename U>
using array_helper = std::array<T, U::value>;
// base
template <typename TypeDescriptor, template<typename, typename...> class Container, typename... Args>
struct PairedTypeGenerator {
using type = std::pair<TypeDescriptor, Container<typename TypeDescriptor::dtype, Args...>>;
};
// std::array
template <typename TypeDescriptor, typename U>
struct PairedTypeGenerator<TypeDescriptor, array_helper, U> {
using type = std::pair<TypeDescriptor, array_helper<typename TypeDescriptor::dtype, U>>;
};
using a = PairedTypeGenerator<Uint8TypeDescriptor, array_helper, Constant<3>>::type;
Full example:
#include <vector>
#include <array>
#include <memory>
struct FloatDataTypeDescriptor {
using dtype = float;
};
struct Uint8TypeDescriptor {
using dtype = unsigned;
};
template <auto X>
struct Constant {
static constexpr auto value = X;
};
template <typename T, typename U>
using array_helper = std::array<T, U::value>;
// base
template <typename TypeDescriptor, template<typename, typename...> class Container, typename... Args>
struct PairedTypeGenerator {
using type = std::pair<TypeDescriptor, Container<typename TypeDescriptor::dtype, Args...>>;
};
// std::array
template <typename TypeDescriptor, typename U>
struct PairedTypeGenerator<TypeDescriptor, array_helper, U> {
using type = std::pair<TypeDescriptor, array_helper<typename TypeDescriptor::dtype, U>>;
};
template <typename T>
class AllocNew : public std::allocator<T>
{};
template <typename T>
class Deleter : public std::default_delete<T>
{};
int main()
{
using a = PairedTypeGenerator<Uint8TypeDescriptor, array_helper, Constant<3>>::type;
using u = PairedTypeGenerator<Uint8TypeDescriptor, std::unique_ptr>::type;
using u2 = PairedTypeGenerator<Uint8TypeDescriptor, std::unique_ptr, Deleter<unsigned>>::type;
using v = PairedTypeGenerator<Uint8TypeDescriptor, std::vector>::type;
using v2 = PairedTypeGenerator<Uint8TypeDescriptor, std::vector, AllocNew<unsigned>>::type;
// array
static_assert(std::is_same_v<a, std::pair<Uint8TypeDescriptor, std::array<unsigned, 3>>>);
// vector
static_assert(std::is_same_v<v, std::pair<Uint8TypeDescriptor, std::vector<unsigned>>>);
static_assert(std::is_same_v<v2, std::pair<Uint8TypeDescriptor, std::vector<unsigned, AllocNew<unsigned>>>>);
// unique_ptr
static_assert(std::is_same_v<u, std::pair<Uint8TypeDescriptor, std::unique_ptr<unsigned>>>);
static_assert(std::is_same_v<u2, std::pair<Uint8TypeDescriptor, std::unique_ptr<unsigned, Deleter<unsigned>>>>);
}
clang1400 -std=c 17