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