I have a simple yet daunting problem I can't solve by myself. I have something like
template<class T, class... Args>
T* create(SomeCastableType* args, size_t numArgs)
{
return new T(static_cast<Args>(args[INDEX_OF_EXPANSION])...);
}
Suppose SomeCastableType
is castable to any type. Obviously what I can't get is that INDEX_OF_EXPANSION
.
Thank you very much for your help.
A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers: You can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.
Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.
Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.
Indices trick, yay~
template<class T, class... Args, std::size_t... Is>
T* create(U* p, indices<Is...>){
return new T(static_cast<Args>(p[Is])...);
}
template<class T, class... Args>
T* create(U* p, std::size_t num_args){
assert(num_args == sizeof...(Args));
return create<T, Args...>(p, build_indices<sizeof...(Args)>{});
}
Of course, I strongly advise using a smart pointer and a std::vector
instead of raw pointers.
You need a helper:
#include <tuple>
template <typename T, bool, typename Tuple, unsigned int ...I>
struct helper
{
static T * go(S * args)
{
return helper<T, sizeof...(I) + 1 == std::tuple_size<Tuple>::value,
Tuple, I..., sizeof...(I)>::go(args);
}
};
template <typename T, typename ...Args, unsigned int ...I>
struct helper<T, true, std::tuple<Args...>, I...>
{
static T * go(S * args)
{
return new T(static_cast<Args>(args[I])...);
}
};
template <typename T, typename ...Args>
T * create(S * args)
{
return helper<T, sizeof...(Args) == 0, std::tuple<Args...>>::go(args);
}
Edit: Tested, seems to work.
With c++17's constexpr if, we can get a much more readable / intelligible implementation of an index-lookup function (I never managed to get my head around the other answers here):
template<typename Target, typename ListHead, typename... ListTails>
constexpr size_t getTypeIndexInTemplateList()
{
if constexpr (std::is_same<Target, ListHead>::value)
return 0;
else
return 1 + getTypeIndexInTemplateList<Target, ListTails...>();
}
This can be used as follows:
size_t index = getTypeIndexInTemplateList<X, Foo,Bar,X,Baz>(); // this will return 2
Or if you have a variadically templated type and want to get an index in it:
template<typename... Types>
class Container
{
public:
size_t getIndexOfType<typename T>() { return getTypeIndexInTemplateList<T, Types...>(); }
};
...
Container<Foo, Bar, X, Baz> container;
size_t container.getIndexOfType<X>(); // will return 2
The way it works is by recursively eliminating types from the list. So the call order for the first example is basically:
getTypeIndexInTemplateList<X, Foo, Bar,X,Baz>() // ListHead = Foo, ListTails = Bar,X,Baz
getTypeIndexInTemplateList<X, Bar, X,Baz>() // ListHead = Bar, ListTails = X, Baz
getTypeIndexInTemplateList<X, X, Baz>() // ListHead = X, so now we return. Recursive addition takes care of calculating the correct index
The function is constexpr, so this will all get executed at compile time, it will just be a constant at runtime.
If you ask for a type that is not present in the list, it will generate a compile error, as it will try to call the function with too few template arguments. And of course, this will just return the index of the first instance of the type in the list, if the type is present more than once.
Suppose
SomeCastableType
is castable to any type. Obviously what I can't get is thatINDEX_OF_EXPANSION
.
Since C++14, you can do the indices trick @Xeo mentioned with the support from the standard library, by using the std::make_index_sequence
helper, as follows:
template<class T, class... Args, std::size_t... Is>
T* create(SomeCastableType* p, std::index_sequence<Is...>)
{
return new T(static_cast<Args>(p[Is])...);
}
template<class T, class... Args>
T* create(SomeCastableType* p, std::size_t num_args)
{
return create<T, Args...>(p, std::make_index_sequence<sizeof...(Args)>());
}
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