C ++ How to create a std :: tuple type based on partial specializations?

I have a pattern with an integer parameter, but the base pattern is disabled on static_assert() as follows. (I only need certain forms of specialization, I want any argument passed to the template to be forbidden, with the exception of certain arguments)

 template<ItemID item_id> struct ItemTemplate{ static_assert(item_id == -1,"Cann't use unspecialized ItemTemplate!"); static ItemID id{ std::numeric_limits<ItemID>::max() }; //... }; 

I also have several specialization forms for this template (I often add or remove some of them)

 template<> struct ItemTemplate<1>{ static constexpr ItemID id{1}; //.. }; template<> struct ItemTemplate<2>{ static constexpr ItemID id{2}; //... }; 

Now I want to create std::tuple , which is initialized only by all available types. Therefore, in the above example, ItemTemplate<1> and ItemTemplate<2> , but not ItemTemplate<3> and other non-specialized types. How to achieve this?

+5
source share
1 answer

I see a way, but only if you forget the static_assert() failure method and define only ItemTemplate specializations.

The following is a simplified example in which I define only some specializations of foo , and the general structure of foo remains undefined.

 template <std::size_t> struct foo; template <> struct foo<2U> {}; template <> struct foo<3U> {}; template <> struct foo<5U> {}; template <> struct foo<7U> {}; 

Now you need to determine something if the type is defined or not; for example next

 template <typename T, std::size_t = sizeof(T)> std::true_type existH (int); template <typename> std::false_type existH (long); template <typename T> using exist = decltype(existH<T>(0)); 

That is: from exist<foo<0>>::value you get false and from exist<foo<2>>::value get true .

Now you need a list (compilation time) of the specialization indices foo , defined from the lower limit (zero, for example) to the upper limit.

You can get it with

 template <std::size_t I, std::size_t topI, typename, bool = (I == topI) || exist<foo<I>>::value> struct fooIndexList; template <std::size_t topI, std::size_t ... Ixs> struct fooIndexList<topI, topI, std::index_sequence<Ixs...>, true> { using type = std::index_sequence<Ixs...>; }; template <std::size_t I, std::size_t topI, std::size_t ... Ixs> struct fooIndexList<I, topI, std::index_sequence<Ixs...>, true> { using type = typename fooIndexList<I+1U, topI, std::index_sequence<Ixs..., I>>::type; }; template <std::size_t I, std::size_t topI, std::size_t ... Ixs> struct fooIndexList<I, topI, std::index_sequence<Ixs...>, false> { using type = typename fooIndexList<I+1U, topI, std::index_sequence<Ixs...>>::type; }; 

Using fooIndexList , getting a std::tuple with all foo set (from zero to the upper limit) is very simple:

 template <std::size_t ... Idx> constexpr auto makeFooTupleH (std::index_sequence<Idx...> const &) { return std::make_tuple( foo<Idx>{} ... ); } constexpr auto makeFooTuple () { return makeFooTupleH( typename fooIndexList<0U, 100U, std::index_sequence<>>::type {}); } 

In the example, the upper limit is 100 , but it can be a makeFooTuple() template parameter.

Below is a complete compilation example

 #include <tuple> #include <utility> #include <iostream> #include <type_traits> template <typename T, std::size_t = sizeof(T)> std::true_type existH (int); template <typename> std::false_type existH (long); template <typename T> using exist = decltype(existH<T>(0)); template <std::size_t> struct foo; template <> struct foo<2U> {}; template <> struct foo<3U> {}; template <> struct foo<5U> {}; template <> struct foo<7U> {}; template <std::size_t I, std::size_t topI, typename, bool = (I == topI) || exist<foo<I>>::value> struct fooIndexList; template <std::size_t topI, std::size_t ... Ixs> struct fooIndexList<topI, topI, std::index_sequence<Ixs...>, true> { using type = std::index_sequence<Ixs...>; }; template <std::size_t I, std::size_t topI, std::size_t ... Ixs> struct fooIndexList<I, topI, std::index_sequence<Ixs...>, true> { using type = typename fooIndexList<I+1U, topI, std::index_sequence<Ixs..., I>>::type; }; template <std::size_t I, std::size_t topI, std::size_t ... Ixs> struct fooIndexList<I, topI, std::index_sequence<Ixs...>, false> { using type = typename fooIndexList<I+1U, topI, std::index_sequence<Ixs...>>::type; }; template <std::size_t ... Idx> constexpr auto makeFooTupleH (std::index_sequence<Idx...> const &) { return std::make_tuple( foo<Idx>{} ... ); } constexpr auto makeFooTuple () { return makeFooTupleH( typename fooIndexList<0U, 100U, std::index_sequence<>>::type {}); } int main () { auto ft = makeFooTuple(); static_assert( std::is_same<decltype(ft), std::tuple<foo<2U>, foo<3U>, foo<5U>, foo<7U>>>{}, "!"); } 

Limitations:

  • this solution only works if generic foo not defined
  • C ++ 14 code; if you need it in C ++ 11, this is a little more complicated.
  • the upper limit in makeFooTuple() cannot be a large number, because fooIndexList is recursive, so the compiler recursion limit limits it; you can get around this limit, but a mode code is required.
+2
source

Source: https://habr.com/ru/post/1271681/


All Articles