I'm trying to write a template function that will perform a set of dynamic_casts() based on its template parameter. I've got the following to illustrate:
class FooData
{
public:
virtual ~FooData() {};
};
class DerivedFooData: public FooData{};
class Other: public FooData{};
void bar(DerivedFooData* d1, DerivedFooData* d2) {}
void bar(DerivedFooData* d1, Other*, DerivedFooData* d2, Other*, Other*) {}
int main()
{
DerivedFooData d1,d2;
std::vector<FooData*> container1{&d1, &d2};
std::vector<FooData*> container2{&d1, &d2};
// I want bar to be called with container1[0] cast to DerivedFooData and container2[1] cast to DerivedFooData
// the first two template params are each container size
foo<1, 1, DerivedFooData, DerivedFooData>(container, container2);
// I want bar to be called with
// container1[0] cast to DerivedFooData
// container1[1] cast to Other
// container2[0] cast to DerivedFooData
// container2[1] cast to Other
// container2[2] cast to Other
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>(container, container2);
}
I can manually create some of those:
template <int N, int M, typename U, typename V>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(output[0]));
}
template <int N, int M, typename U, typename V, typename W, typename X, typename Y>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
bar(dynamic_cast<U*>(input[0]), dynamic_cast<V*>(input[1]), dynamic_cast<W*>(output[0]), dynamic_cast<X*>(output[1]), dynamic_cast<Y*>(output[2]));
}
But I can't figure out how to specify in a generic way all combinations of N and M. I assume variadic templates will come in somewhere, but I would need some guidance.
Not elegant at all (and not sure about the exact indexes) but... something as follows (given that you can use C++14) should works (if I understand correctly what do you want)
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
I know that C++20 is too recent for you but, just for fun, I show you how use the new C++20 lambda-template feature to avoid the use of the helper function
// from C++20: foo_helper() isn't needed anymore
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ [&]<std::size_t ... Is>(std::index_sequence<Is...>)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
(std::make_index_sequence<Dim1+Dim2>{}); }
The following is a full compiling C++14 example
#include <vector>
#include <type_traits>
struct FooData { virtual ~FooData() {}; };
class DerivedFooData: public FooData { };
class Other : public FooData { };
void bar (DerivedFooData*, DerivedFooData*) {}
void bar (DerivedFooData*, Other*, DerivedFooData*, Other*, Other*) {}
template <std::size_t Dim1, typename ... Ts, std::size_t ... Is>
void foo_helper (std::index_sequence<Is...>, std::vector<FooData*> inV,
std::vector<FooData*> outV)
{ bar( dynamic_cast<Ts*>(Is < Dim1 ? inV[Is] : outV[Is-Dim1])... ); }
template <std::size_t Dim1, std::size_t Dim2, typename ... Ts>
void foo (std::vector<FooData*> inV, std::vector<FooData*> outV)
{ foo_helper<Dim1, Ts...>
(std::make_index_sequence<Dim1+Dim2>{}, inV, outV); }
int main ()
{
DerivedFooData d1, d2, d3;
std::vector<FooData*> container1 {&d1, &d2};
std::vector<FooData*> container2 {&d1, &d2, &d3};
foo<1, 1, DerivedFooData, DerivedFooData>(container1, container2);
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
(container1, container2);
}
The basic idea behind any template recursion is to handle the arguments one at a time, recurse on the function with one input type argument removed, and then terminate when the template argument list is empty.
A common way to handle two variadic type lists is to define a "pack" type that you can specialize on, where the pack takes a variable number of template arguments. This gives you the ability to easily separate multiple sets of variadic type arguments.
Here, I demonstrate this example by declaring type_pack without any implementation (which is fine, we only use it as a type and we never instantiate it) and then I declare multiple specializations of foo_fn that are designed to:
Peel the first type off of one of the two lists and perform the dynamic_cast.
Determine the next step by removing the first type argument to the relevant pack type.
Transition to the second pack when the first one becomes empty.
Pass the processed (cast) arguments through to the next step.
Finally, call bar() with the computed arguments when both packs are empty.
template <typename...>
struct type_pack;
// Base declaration that we will specialize.
template <int, int, typename, typename>
struct foo_fn;
// Specialization handling when the first pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T1, typename... T2>
struct foo_fn<OffsetA, OffsetB, type_pack<THead, T1...>, type_pack<T2...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA + 1,
OffsetB,
type_pack<T1...>,
type_pack<T2...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(input[OffsetA]));
}
};
// Specialization handling when the first pack is empty and the second
// pack is not empty.
template <int OffsetA, int OffsetB, typename THead, typename... T>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<THead, T...>> {
template <typename... Args>
static void f(foovec_t const & input, foovec_t const & output, Args && ... args) {
return foo_fn<
OffsetA,
OffsetB + 1,
type_pack<>,
type_pack<T...>
>::f(input, output, std::forward<Args>(args)..., dynamic_cast<THead *>(output[OffsetB]));
}
};
// Specialization handling the terminating case (all packs empty).
template <int OffsetA, int OffsetB>
struct foo_fn<OffsetA, OffsetB, type_pack<>, type_pack<>> {
template <typename... Args>
static void f(foovec_t const &, foovec_t const &, Args && ... args) {
bar(std::forward<Args>(args)...);
}
};
// Helper type to provide the two initial integer values.
template <typename, typename>
struct foo;
template <typename... T1, typename... T2>
struct foo<type_pack<T1...>, type_pack<T2...>> {
static void f(foovec_t const & input, foovec_t const & output) {
foo_fn<0, 0, type_pack<T1...>, type_pack<T2...>>::f(input, output);
}
};
You would call this like foo<type_pack<DerivedFooData, Other>, type_pack<DerivedFooData, Other, Other>>::f(container, container2) in your second example. Note that you don't have to provide any sizes; these are inferred from the size of each pack.
See this demo and note that the pointer arguments where the type doesn't match come through as null.
I don't attempt to define bar() as I assume you have already done this, or know how to do it. The bar() in my example only accepts specific pointer types (for the purposes of testing that the casts were correctly performed).
This code uses only C++11 features.
Note that std::forward is not strictly necessary because the cast values are always pointers. However, it's good to get in the habit of using it when forwarding a variable-size argument list. If the values were huge strings/vectors then forwarding at each step would eliminate a ton of useless copying.
I prefer "pack type" syntax instead of give extra numbers, so
foo<std::tuple<DerivedFooData, Other>, std::tuple<DerivedFooData, Other, Other>>
instead of your:
foo<2, 3, DerivedFooData, Other, DerivedFooData, Other, Other>
You can still do:
template <typename Tuple, std::size_t ... Is1, std::size_t ... Is2, typename ... Ts>
void foo(std::index_sequence<Is1...>, std::index_sequence<Is2...>, Ts&&...args)
{
foo<std::tuple<std::tuple_element_t<Is1, Tuple>...>,
std::tuple<std::tuple_element_t<sizeof...(Is1) + Is2, Tuple>...>>(
std::forward<Ts>(args)...);
}
template <std::size_t N1, std::size_t N2, typename ... Ts>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
static_assert(N1 + N2 == sizeof...(Ts), "!");
foo<std::tuple<Ts...>>(std::make_index_sequence<N1>{},
std::make_index_sequence<N2>{},
input,
output);
}
to use your syntax.
Now using a primary helper to cast each vector element inside a tuple
template <typename Pack> struct dynamic_cast_as_tuple;
template <typename ...Ts>
struct dynamic_cast_as_tuple<std::tuple<Ts...>>
{
template <typename T>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v) const
{
return (*this)(v, std::index_sequence_for<Ts...>{});
}
private:
template <typename T, std::size_t ... Is>
std::tuple<Ts*...> operator ()(const std::vector<T*>& v, std::index_sequence<Is...>) const
{
return {dynamic_cast<Ts*>(v[Is])...};
}
};
And then, wanted function is:
template <typename pack1, typename pack2>
void foo(const std::vector<FooData*>& input, const std::vector<FooData*>& output)
{
std::apply([](auto*... ps){ bar(ps...); },
std::tuple_cat(
dynamic_cast_as_tuple<pack1>{}(input),
dynamic_cast_as_tuple<pack2>{}(output))
);
}
Demo
std::index_sequence is C++14 and std::apply c++17, but can be implemented in C++11.
Related
I am trying to create a variadic template function which would call a function to consecutive pairs of arguments.
The desired function signature would be:
template <typename ...Ts>
void apply(Ts &...args);
When called with apply(t1, t2, t3) the function should make a sequence of calls func(t1, t2)and func(t2, t3), where func is a function with signature:
template <typename L, typename R>
void func(L &left, R &right);
The order of operations is not really relevant in my context. The function has to be able to modify objects left and right, hence passed by reference. I cannot simply use polymorphic access through a base class pointer, since the objects have different class templates, a shared class cannot really be taken out.
Is it possible to achieve such a sequence of calls via a variadic template function? None of the pack expansion and fold expression examples that I've seen seem to cover such a scenario. Or should I pass my objects in a different fashion?
My initial attempt, included below (with some details omitted), packed all template parameters into a tuple, and then used a ‘const for-loop’ to ‘loop’ through the tuple elements. However, I soon came to realize that this approach would not work, because the lambda in the const-for loop invokes operator() const and therefore cannot modify the passed objects.
The code I was using does make the desired sequence of calls, but the objects are not modified (set_something() is not a const function). I had to resort to using a wrapper function with different numbers of template parameters, and making the calls to func manually.
template <std::size_t Begin, typename Callable, std::size_t... I>
constexpr void const_for_impl(Callable &&func, std::index_sequence<I...>) {
(func(std::integral_constant<std::size_t, Begin + I>{}), ...);
}
template <std::size_t Begin, std::size_t End, typename Callable>
constexpr void const_for(Callable &&func) {
const_for_impl<Begin>(std::forward<Callable>(func),
std::make_index_sequence<End - Begin>{});
};
template <typename... Ts>
void apply(Ts *... args) {
auto tuple = std::make_tuple(std::forward<Ts>(args)...);
const_for<0, sizeof...(args) - 1>(
[&](auto I) { func((std::get<I>(tuple)), (std::get<I + 1>(tuple))); });
};
template <typename L, typename R>
void func(L &l, R &r) {
// Validate with some type traits
static_assert(has_some_property<L>::value);
static_assert(has_another_property<R>::value);
// Get a shared pointer to something common
auto common = std::make_shared<typename something_common<L, R>::type>();
l.set_something(common);
r.set_something(common);
};
// Application scenario
int main() {
ComplexObjectA<SomeType, SomeParameter> a;
ComplexObjectB<AnotherType, AnotherParameter> b;
ComplexObjectC c;
apply(a, b, c);
return 0;
}
So, what's the problem? Simple fold-like template (and remember that template pattern-matching goes in reverse!)
template<typename T1, typename T2>
void apply(T1 &&t1, T2 &&t2) { func(t1, t2); }
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(t1, t2);
return apply(t2, ts...);
}
Or, more precise, it should actually look as (thanks #MaxLanghof):
void apply(T1 &&t1, T2 &&t2) {
func(std::forward<T1>(t1), std::forward<T2>(t2));
}
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(std::forward<T1>(t1), t2);
return apply(std::forward<T2>(t2), std::forward<TS>(ts)...);
}
An alternative (c++14) approach:
#include <utility>
#include <utility>
#include <tuple>
#include <iostream>
template <typename L, typename R>
void func(L &left, R &right) {
std::cout << left << " " << right << std::endl;
}
template <typename Tup, std::size_t... Is>
void apply_impl(Tup&& tup, std::index_sequence<Is...>) {
int dummy[] = { 0, (static_cast<void>(func(std::get<Is>(tup), std::get<Is + 1>(tup))), 0)... };
static_cast<void>(dummy);
}
template <typename ...Ts>
void apply(Ts &...args) {
apply_impl(std::forward_as_tuple(args...), std::make_index_sequence<sizeof...(Ts) - 1>{});
}
int main() {
int arr[] = {0, 1, 2, 3};
apply(arr[0], arr[1], arr[2], arr[3]);
}
Output:
0 1
1 2
2 3
[online example]
To make it c++11 compliant one would need to use one of the available integer_sequence implementations.
I'm trying to implement tuples with template metaprogramming, but am having problems with the indexing function get. The implementation of Tuple type is this:
template<typename A, typename... B>
class Tuple : Tuple<B...> {
private:
A val;
public:
using Base = Tuple<B...>;
Base* base() {
return static_cast<Base*>(this);
}
const Base* base() const {
return static_cast<const Base*>(this);
}
Tuple(A a, B... b): Base(b...), val(a) { }
A first() {
return val;
}
};
template<class A>
class Tuple<A> {
private:
A val;
public:
Tuple(A a): val{a} {}
A first() {
return val;
}
};
The implementation of get structure is:
template<int N, class... A>
struct get {
select<N,A...> operator()(Tuple<A...> t) {
return get<N-1>()(t.base());
}
};
template<class R, class... A>
struct get<0> {
R operator()(Tuple<R, A...> t) {
return t.first();
}
};
This is the error compiler is giving me:
tuple.cpp:53:8: error: partial specialization of 'get' does not use any of its template parameters
struct get<0> {
^
1 error generated.
Why am I getting this error? How can I correct it?
Note: select<N,A...> is a type function which selects type at Nth index from A.
Your get's primary template is:
template<int N, class... A>
struct get{ ... };
your get's partial specialization is:
template<class R, class... A>
struct get<0>{ ... };
The specialization is receiving a single template argument, i.e.: 0, but the primary template above takes two template parameters:
the non-type template parameter N.
the variadic type parameter A.
Besides, how can R be deduced?
Specializing get instead as:
template<class R, class... A>
struct get<0, R, A...>{ ... };
will make possible R to be deduced: it will be deduced to the type of the first element of the passed variadic argument. For example, in:
get<0, int, float, double> foo;
R will be deduced to int.
Maybe you have to partial specialize get as follows
template<class R, class... A>
struct get<0, R, A...> {
R operator()(Tuple<R, A...> t) {
return t.first();
}
};
I mean... get<0, R, A...>, not get<0>
But you have also to modify the main get to call the following call with the correct type list, so
template<int N, typename A0, typename ... As>
struct get {
auto operator()(Tuple<A0, As...> t) {
return get<N-1, As...>()(t.base());
}
};
Otherwise you can also demand the types management to a template version of the operator() and maintain only the int N value for get
template <int N>
struct get
{
template <typename Tpl>
auto operator() (Tpl t)
-> decltype( get<N-1>()(t.base()) )
{ return get<N-1>()(t.base()); }
};
template<>
struct get<0>
{
template <typename Tpl>
auto operator() (Tpl t)
-> decltype ( t.first() )
{ return t.first(); }
};
Starting from C++14 you can avoid the decltype() part.
Off topic suggestion: avoid the use of names that can collide with std namespace names.
Maybe myGet and myTuple instead of get and Tuple.
Otherwise you can put all in a personal namespace (so myNs::get and myNs::Tuple.
Suppose I have the following function, that takes a function as a parameter.
template <typename F>
void test_func(F f)
{
// typedef typename function_traits<F>::return_type T;
typedef int T;
std::mt19937 rng(std::time(0));
std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());
f(uint_dist10(rng), uint_dist10(rng)); // Problem!
}
Usage would be:
int foo(int, int) { return 0; }
int bar(int, int, int, int) { return 0; }
int main()
{
test_func(foo);
// test_func(bar);
}
Just like foo and bar, I have several functions that return T, and take some amount of parameters of type T. I would like test_func to generate as many calls to my RNG as the function f takes parameters. In other words, we can assume T is always an integer type, and that each parameter will be the same, i.e. a function call to an RNG.
Using function_traits (such as the ones in Boost), I can fetch the return type of F, and that helps a little. Roughly, my question is
How can I generate a needed amount of function calls so that it matches the arity of the function F?
Before C++11, I would have looked at Boost.Preprocessor, or maybe relied on template specialization. Is there a nicer way of doing it now?
First define a meta function called arity to compute arity of the function (it is just a simple implementation; can be improved to compute arity of functors also. See my answer here.):
template<typename F>
struct arity;
template<typename R, typename ...Args>
struct arity<R (*)(Args...)>
{
static const std::size_t value = sizeof ... (Args);
};
then define another meta function called genseq to generate a compile time sequence of integral values:
template<int ... N>
struct seq
{
using type = seq<N...>;
template<int I>
struct push_back : seq<N..., I> {};
};
template<int N>
struct genseq : genseq<N-1>::type::template push_back<N-1> {};
template<>
struct genseq<0> : seq<> {};
template<int N>
using genseq_t = typename genseq<N>::type; //Just a friendly alias!
then a function invoker as:
template<typename F, typename ArgEvaluator, int ...N>
void invoke(seq<N...>, F f, ArgEvaluator arg_evaluator)
{
using arg_type = decltype(arg_evaluator());
constexpr std::size_t arity = sizeof ... (N);
arg_type args[] { (N, arg_evaluator()) ... }; //enforce order of evaluation
f( args[N] ... );
}
And then your code would become this:
template <typename F>
void test_func(F f)
{
// typedef typename function_traits<F>::return_type T;
typedef int T;
std::mt19937 rng(std::time(0));
std::uniform_int_distribution<T> uint_dist10(0, std::numeric_limits<T>::max());
//f(uint_dist10(rng), uint_dist10(rng)); // Problem!
auto arg_evaluator = [&]() mutable { return uint_dist10(rng); };
invoke(genseq_t<arity<F>::value>(), f, arg_evaluator);
}
Here is a sample demo.
Hope that helps.
No need for complicated meta calculations.
template <typename Ret, typename ... T>
void test_func (Ret f (T...))
{
std::mt19937 rng(std::time(0));
f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...);
}
int moo(int, int, int){ return 0; }
int main ()
{
test_func(moo);
}
To support functors one needs a bit longer implementation, still not too complicated:
// separate arguments type from function/functor type
template <typename F, typename ... T>
void test_func_impl (F f)
{
std::mt19937 rng(std::time(0));
f((std::uniform_int_distribution<T>(0, std::numeric_limits<T>::max())(rng))...);
}
// overload for a straight function
template <typename Ret, typename ... T>
void test_func (Ret f (T...))
{
test_func_impl<decltype(f), T...>(f);
}
// forwarder for a functor with a normal operator()
template <typename F, typename Ret, typename... T>
void test_func_for_functor (F f, Ret (F::*)(T...))
{
test_func_impl<F, T...>(f);
}
// forwarder for a functor with a const operator()
template <typename F, typename Ret, typename... T>
void test_func_for_functor (F f, Ret (F::*)(T...)const)
{
test_func_impl<F, T...>(f);
}
// overload for anything that has operator()
template <typename F>
void test_func (F f)
{
test_func_for_functor(f, &F::operator());
}
In my engine, I have simple reflection system filled with classes info at compile time (that is, built around set of templates, that allows me to automate the process of generating metainfo).
Consider following example:
class Type
{
//...
Map<PropertyHash, TypeProperty> _properties;
};
For each type there is a function:
template <class T>
void InitializeType(TypeInitializer* typeInitializer);
responsible for type initialization. TypeInitializer has few methods used to add fields and base types to type. So basically, every new type requires only specialization of this function. Later, when type is queried for the first time, TypeDatabase creates concrete Type object and calls InitializeType() for it (TypeInitializer gets pointer to type during construction). For example:
struct CST
{
const float* fptr;
volatile Uint32 vuint;
void** vptr;
};
template <>
SvrInline void InitializeType<CST>(TypeInitializer* typeInitializer)
{
typeInitializer->AddProperty("fptr", &CST::fptr);
typeInitializer->AddProperty("vuint", &CST::vuint);
typeInitializer->AddProperty("vptr", &CST::vptr);
}
And that's it. All magic is done in TypeProperty constructor, which is declared as:
template <class Object_type, class Property_type>
TypeProperty(const char* fieldName, Property_type (Object_type::* propertyPtr));
This allows me to know the exact type of the property. I test it for size, const-ness, volatile-ness etc., and save this info in TypeProperty object. Nice.
Now, I need something identical for members function as well. 'Identical' means, that I can add function in the very same way I'm adding properties right now.
My first thought were variadic templates (my engine is built with full support for C++11 features in mind):
template <typename Object_t, typename Return_t, typename... Args>
TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
{
//What now?
}
I do not know, however, how should I extract types from args. I saw an article with an approach, that uses function overloading:
template <typename P, typename R, typename Arg1, typename... Args>
void Func(R (P::*)(Arg1 arg1, Args&&... args))
{
}
template <typename P, typename R, typename... Args>
void Func(R (P::*)(Args&&... args))
{
}
template <typename P, typename R>
void Func(R (P::*)())
{
}
Function was 'forwarded' recursively (I know it's not an actual recursion) with one parameter less each time. I don't see, however, how is this suitable for my case.
There's no need for recursion, just use pack expansion:
template <typename Object_t, typename Return_t, typename... Args>
TypeMethod(const char* methodName, Return_t (Object_t::*)(Args&&... args)
{
setName(methodName);
setObjectType<Object_t>();
setReturnType<Return_t>();
auto dummy[] = {0, (addArgumentType<Args>(), 0)...};
}
We place the pack expansion inside a braced-init-list to ensure that the calls to addArgumentType<...> are made in the correct order.
...
template <typename P, typename R, typename Arg1, typename Arg2>
void Func(R (P::*)(Arg1 arg1, Arg2 arg2))
{
// function type is R (P::*)(Arg1 arg1, Arg2 arg2)
}
template <typename P, typename R, typename Arg1>
void Func(R (P::*)(Arg1 arg1))
{
// function type is R (P::*)(Arg1 arg1)
}
template <typename P, typename R>
void Func(R (P::*)())
{
// function type is R (P::*)()
}
I am not familiar with variadic args. This was an only solution before C++11. But now new features of C++11 may solve this problem more elegantly.
BTW. First I saw this way of resolving signature in boost.pyton library implementation.
Using decompose_mem_fun_ptr from http://coliru.stacked-crooked.com/a/00750bf7564ab6d4
template <typename M>
TypeMethod(const char* methodName, M&m)
{
setName(methodName);
setObjectType<typename decompose_mem_fun_ptr<M>::class_type>();
setReturnType<typename decompose_mem_fun_ptr<M>::return_type>();
// use other info from decompose_mem_fun_ptr<M>.
using args_type = typename decompose_mem_fun_ptr<M>::arguments;
internal_setArgs<args_type>(make_index_sequence<std::tuple_size<args_type>::value>());
}
template<typename Tuple, std::size_t...Is>
void internal_setArgs(index_sequence<Is...>)
{
// Assuming setArg<T>(i_th).
int dummy[] = {0, (setArg<typename std::tuple_element<Is, Tuple>::type>(Is), 0)...};
static_cast<void>(dummy); // silent warning about unused variable.
}
for index_sequence and make_index_sequence:
#if 1 // Not in C++11
#include <cstdint>
template <std::size_t ...> struct index_sequence {};
template <std::size_t N, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < N - 1, N - 1, Is... > {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
Question is simple, how would I implement a function taking a variable number of arguments (alike the variadic template), however where all arguments have the same type, say int.
I was thinking about something alike this;
void func(int... Arguments)
Alternatively wont a recursive static assert on the types work?
A possible solution is to make the parameter type a container that can be initialized by a brace initializer list, such as std::initializer_list<int> or std::vector<int>. For example:
#include <iostream>
#include <initializer_list>
void func(std::initializer_list<int> a_args)
{
for (auto i: a_args) std::cout << i << '\n';
}
int main()
{
func({4, 7});
func({4, 7, 12, 14});
}
Here's a version that removes the function from the overload set, instead of giving a static_assert. This is allows you to provide other overloads of the function that could be used when the types aren't all the same, rather than a fatal static_assert that can't be avoided.
#include <type_traits>
template<typename... T>
struct all_same : std::false_type { };
template<>
struct all_same<> : std::true_type { };
template<typename T>
struct all_same<T> : std::true_type { };
template<typename T, typename... Ts>
struct all_same<T, T, Ts...> : all_same<T, Ts...> { };
template<typename... T>
typename std::enable_if<all_same<T...>::value, void>::type
func(T...)
{ }
If you want to support perfect forwarding you probably want to decay the types before checking them, so that the function will accept a mix of lvalue and rvalue arguments as long as they have the same type:
template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }
Alternatively, if you have a general purpose trait for testing the logical conjunction you can do it using std::is_same instead of writing your own all_same:
template<typename T, typename... Ts>
typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
func(T&&, Ts&&...)
{ }
Because this requires at least one argument you'd also need another overload to support the zero-argument case:
void func() { }
The and_ helper can be defined like so:
template<typename...>
struct and_;
template<>
struct and_<>
: public std::true_type
{ };
template<typename B1>
struct and_<B1>
: public B1
{ };
template<typename B1, typename B2>
struct and_<B1, B2>
: public std::conditional<B1::value, B2, B1>::type
{ };
template<typename B1, typename B2, typename B3, typename... Bn>
struct and_<B1, B2, B3, Bn...>
: public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
{ };
I think you can do this by specifying a concrete type when chewing your arguments out of the argument pack. Something like:
class MyClass{};
class MyOtherClass{};
void func()
{
// do something
}
template< typename... Arguments >
void func( MyClass arg, Arguments ... args )
{
// do something with arg
func( args... );
// do something more with arg
}
void main()
{
MyClass a, b, c;
MyOtherClass d;
int i;
float f;
func( a, b, c ); // compiles fine
func( i, f, d ); // cannot convert
}
In the generic case void func( MyClass arg, Arguments ... args ) would become void func( arg, Arguments ... args ) with a template type T.
#Skeen
How about this?
template <typename T>
void func_1(std::initializer_list<T>&& a) {
// do something
}
template <typename... T>
void func(T&&... a) {
func_1({std::forward<T>(a)...});
}
int main() {
func(1, 2, 3);
// func(1, 2, 3, 4.0); // OK doesn't compile
}
If you don't want to use brace-based initializer_list/vector and want to keep the arguments separate in form of argument pack, then below solution checks it at compile time using recursive static_asserts:
#include<type_traits>
template<typename T1, typename T2, typename... Error>
struct is_same : std::false_type {};
template<typename T, typename... Checking>
struct is_same<T, T, Checking...> : is_same<T, Checking...> {};
template<typename T>
struct is_same<T,T> : std::true_type {};
template<typename... LeftMost>
void func (LeftMost&&... args)
{
static_assert(is_same<typename std::decay<LeftMost>::type...>::value,
"All types are not same as 'LeftMost'");
// ...
}
int main ()
{
int var = 2;
func(1,var,3,4,5); // ok
func(1,2,3,4.0,5); // error due to `static_assert` failure
}
Actually this solution would check all the arguments with respect to the first argument. Suppose it was double then everything would be checked against double.
Because I don't think I saw this solution, you could write a specific function for every type (in your case, just int) then a forwarding function taking variadic argument types.
Write each specific case:
then for each specific case:
// only int in your case
void func(int i){
std::cout << "int i = " << i << std::endl;
}
Then your forwarding function like this:
template<typename Arg0, typename Arg1 typename ... Args>
void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
func(std::forward<Arg0>(arg0));
func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
This is good because it is expandable for when you want to accept maybe another type too.
Used like this:
int main(){
func(1, 2, 3, 4); // works fine
func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}