How to Create a Type List (For Variadic Templates) That Contains N-Times the Same Type

How to create a type list (for variadic templates) that contains n-times the same type?

I would do it this way:

template<typename, typename>
struct append_to_type_seq { };

template<typename T, typename... Ts, template<typename...> class TT>
struct append_to_type_seq<T, TT<Ts...>>
{
using type = TT<Ts..., T>;
};

template<typename T, unsigned int N, template<typename...> class TT>
struct repeat
{
using type = typename
append_to_type_seq<
T,
typename repeat<T, N-1, TT>::type
>::type;
};

template<typename T, template<typename...> class TT>
struct repeat<T, 0, TT>
{
using type = TT<>;
};

As a small test:

#include <type_traits>
#include <tuple>

template<typename... Ts>
struct X { };

int main()
{
repeat<double, 5, std::tuple>::type t = std::make_tuple(1., 2., 3., 4., 5.);
static_assert(
std::is_same<
decltype(t),
std::tuple<double, double, double, double, double>
>::value, "!");

repeat<double, 3, X>::type y;
static_assert(
std::is_same<decltype(y), X<double, double, double>>::value, "!");
}

Finally, a live example.

How to create a variadic struct that takes a variadic invocable of the same arity as the ctor parameter?

std::tuple (with std::apply) might help:

template <std::derived_from<IUnknown>... Ts>
struct AutoManagedCOMObj
{
std::tuple<Ts*...> tuple_ptrs;

template<std::invocable<Ts**...> Invocable>
AutoManagedCOMObj(Invocable initializer)
{
HRESULT hr = std::apply([&](auto*&... ptrs){ return initializer(&ptrs...);},
tuple_ptrs);
if (FAILED(hr)) exit(hr);
}
~AutoManagedCOMObj()
{
std::apply([](auto*... ptrs){ (ptrs->Release(), ...); }, tuple_ptrs);
}
};

Ensuring that a variadic template contains no duplicates

This can be written with the help of two metafunctions.

First, IsContained checks whether a type appears in a type list.

template <typename T, typename... List>
struct IsContained;

template <typename T, typename Head, typename... Tail>
struct IsContained<T, Head, Tail...>
{
enum { value = std::is_same<T, Head>::value || IsContained<T, Tail...>::value };
};

template <typename T>
struct IsContained<T>
{
enum { value = false };
};

Second, IsUnique checks whether a type list contains no duplicates. It uses IsContained to check all element combinations.

template <typename... List>
struct IsUnique;

template <typename Head, typename... Tail>
struct IsUnique<Head, Tail...>
{
enum { value = !IsContained<Head, Tail...>::value && IsUnique<Tail...>::value };
};

template <>
struct IsUnique<>
{
enum { value = true };
};

With these tools, the static assertion is then very simple:

template <typename... Ts>
struct NoDuplicates
{
static_assert(IsUnique<Ts...>::value, "No duplicate types allowed");
};

Best way to generate a variadic argument list containing N arguments of a given type?

Something along these lines, perhaps:

template <typename T, size_t dummy>
using EatIndex = T;

template <typename T, std::size_t... I>
Bag<EatIndex<T, I>...> MakeBagN(std::index_sequence<I...>);

template<size_t N, typename T>
using BagN = decltype(MakeBagN<T>(std::make_index_sequence<N>()));

template<size_t N, typename T>
class UniBag : public BagN<N, T> {};

Demo

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

You can just accept the arguments by the variadic template and let typechecking check the validity later on when they are converted.

You can check convertibility on the function interface level though, to make use of overload resolution for rejecting outright wrong arguments for example, by using SFINAE

template<typename R, typename...> struct fst { typedef R type; };

template<typename ...Args>
typename fst<void,
typename enable_if<
is_convertible<Args, ToType>::value
>::type...
>::type
f(Args...);

For your use-case if you know the steps to go from an std::array<> to your dragon_list_t then you have already solved it though according to the first option above ("convert-later"):

template<typename ...Items>
dragon_list_t make_dragon_list(Items... maidens) {
std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};
// here be dragons
}

If you combine this with the above is_convertible approach you have a reject-early template that also does overload resolution on arguments and rejects them if not applicable.

Expand a type N times in template parameter

You can use std::index_sequence:

template<std::size_t N, typename = std::make_index_sequence<N>>
struct A;

template<std::size_t N, std::size_t... S>
struct A<N, std::index_sequence<S...>> {
std::function<std::size_t(decltype(S)...)> foo;
};

Live example

If you like, you could also define to what type it expands:

template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct A;

template<typename T, std::size_t N, std::size_t... S>
struct A<T, N, std::index_sequence<S...>> {
template<std::size_t>
using type = T;

std::function<std::size_t(type<S>...)> foo;
};

C++ variadic templates with type and non-type argument mixing for recursive inheritance

It's not possible. You'll have to settle for one of the following:

Foo<Bar<int, 5>, Bar<double, 7>> foo_1;
Foo<int, Bar<5>, double, Bar<7>> foo_1;
// ...?

If the values are always integral, you could also try this:

Foo<int[5], double[7]> foo_1;

And then extract elemenet types & extents from each argument.

Variadic template declaration from single integer argument?

You can write a meta-function that accepts N, base_t, and SomeOtherClass, and recursively calls itself with a smaller N, each time tacking on base_t to the end of a growing parameter pack:

template <int N, typename T, template<typename...> typename C, typename ...Ts>
struct expand {
using type = typename expand<N-1, T, C, T, Ts...>::type;
};

For the base case, when N goes down to 0, the meta-function yields SomeOtherClass instantiated with the parameter pack of N base_t types:

template <typename T, template<typename...> typename C, typename ...Ts>
struct expand<0, T, C, Ts...> {
using type = C<Ts...>;
};

Also, convenience alias is nice to avoid having to say typename at the call site:

template <int N, typename T, template<typename...> typename C>
using expand_t = typename expand<N, T, C>::type;

Now at the call site you can write:

expand_t<N, base_t, SomeOtherClass> foo; 
// equivalent to
// SomeOtherClass< base_t, base_t, ..., base_t > foo;
// ^^^ N times ^^^

Here's a demo.



Related Topics



Leave a reply



Submit