I have a template with an integer parameter, but the base template is disabled by static_assert() like this. (I only want some certain specialization forms; I want any argument passed to the template to be prohibited except for 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 specializations form for this template like (I often add or delete 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 a std::tuple which is initialised only by all available types. So in the above example, ItemTemplate<1> and ItemTemplate<2>, but not ItemTemplate<3> and other non-specialized types. How do I achieve this?
I see a way but only if you forget the static_assert() denial way and define only the specializations of ItemTemplate.
The following is a simplified example where I define only some specializations of foo and the foo generic struct remain 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 something to detect if a type is defined or not; by example, the following
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 you get true.
Now you need a list (usable compile time) of indexes of foo specialization defined from a lower limit (zero, by example) to an upper limit.
You can obtain 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, obtaining a std::tuple with all the foo defined (from zero to an 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 can be a template parameter of makeFooTuple().
The following is a full compiling 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>>>{}, "!");
}
Limits:
foo isn't definedmakeFooTuple() can't be a big number because fooIndexList is recursive so is limited by the compiler's recursion limit; you can bypass this limit but require mode code.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With