Variadic templates compiler error

Hey guys,

I'm having some troubles with Variadic templates.

I got a generic class with a member function to modify its data


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename Mutex_Type_T, typename Struct_Type_T>
class CGlobal_Struct
{
public:

	template<typename Functor_T, typename ... Args_T>
	bool Modify(Functor_T fun, const Args_T&... args, const uint32_t mutex_timeout);

private:
     Mutex_Type_T mutex =  {};

     Struct_Type_T data = { };
}


template<typename Mutex_Type_T, typename Struct_Type_T>
template<typename Functor_T, typename ... Args_T>
bool CGlobal_Struct<Mutex_Type_T, Struct_Type_T>::Modify(Functor_T fun, const Args_T&... args, const uint32_t mutex_timeout)
{
	CLock_Guard lock(mutex);
	if (false == lock.Lock(mutex_timeout))
	{
	     return false;
	}

	bool res = fun(data, args...);

	return res;
}



1
2
3
4
volatile auto res4 = instance.test_global.Modify(Mod2,1.0f,1000);

//with
bool Mod2(C_Test_CConfiguration::Test_Struct_T& data, float a);



Now if i look at the output I'm getting this error
1
2
3
4
5
6
7
8
9
error: no matching function for call to 'NGlobal_Info::CFree_RTOS_Global_Info<NTestcases::C_Test_CConfiguration::Test_Struct_T>::Modify(bool (&)(NTestcases::C_Test_CConfiguration::Test_Struct_T&, float), float, int)'
  volatile auto res4 = instance.test_global.Modify(Mod2,1.0f,1000);
                                                                 ^
In file included from .xxx note: candidate: template<class Functor_T, class ... Args_T> bool NGlobal_Info::CGlobal_Struct<Mutex_Type_T, Struct_Type_T>::Modify(Functor_T, const Args_T& ..., uint32_t) [with Functor_T = Functor_T; Args_T = {Args_T ...}; Mutex_Type_T = NGlobal_Info::CFree_RTOS_Mutex; Struct_Type_T = NTestcases::C_Test_CConfiguration::Test_Struct_T]
  bool Modify(Functor_T fun, const Args_T&... args, const uint32_t mutex_timeout);
       ^~~~~~
xxx note:   template argument deduction/substitution failed:
xxx: note:   candidate expects 2 arguments, 3 provided


Now if you look closely you will notice an extra float in
Modify(bool (&)(NTestcases::C_Test_CConfiguration::Test_Struct_T&, float),
float, int)'

This should not be there. I'd expect it to be more like this
Modify(bool (&)(NTestcases::C_Test_CConfiguration::Test_Struct_T&, float), int)'

Where does this extra float come from?
Last edited on
The "extra" float is the 1.0f that you passed in.

Try moving args to the end of the parameters. I think it might need to be last.
Dutch has the right answer, because....

Parameter packs only undergo template argument deduction if they're the last in the parameter list, or if the following parameters have default arguments.
Last edited on
thx you 2 that was indeed the correct hint :)
Removing the const uint32_t mutex_timeout at the end seems to work.
However if i move the mutex_timeout parameter to the front i'd have to update all my function calls


mbozzi you mentioned that i pass mutex_timeout with a default parameter at the end it should work? What do you mean.

I tried
1
2
template<typename Functor_T, typename ... Args_T, typename Mutex_Timeout_T = uint32_t>
	bool Modify(Functor_T fun, const Args_T&... args, const Mutex_Timeout_T mutex_timeout = 100);


and that

1
2
template<typename Functor_T, typename ... Args_T>
	bool Modify(Functor_T fun, const Args_T&... args, const uint32_t mutex_timeout = 100);


both with no success.
I think you need to change your function calls. I'm not sure but mbozzi may have misspoke about the default value making it okay to put the parameter after the parameter pack.
I'm not sure but mbozzi may have misspoke about the default value making it okay to put the parameter after the parameter pack.

I should have been more clear. By default arguments I meant default template arguments. That doesn't help OP here.

1
2
3
4
template <typename... Ts, typename U = void> 
void f(Ts&&...); // fine: Ts... is deducible
template <typename... Ts, typename U> 
void g(Ts&&..., U); // useless 
g is useless because it can never be instantiated.
It isn't possible to specify where the parameter pack ends.

Changing your function call order would be the best option. If this isn't possible, you can extract the last argument in the parameter pack and treat it as the mutex_timeout.

Here's one way to do this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// call fn with all but the last element of args...
template <typename Fn, typename... Args>
void f(Fn fn, Args... args)
{
  // put all N arguments into a tuple
  // then write out all the indices for the first N - 1 arguments
  // (i.e., 0, 1, 2, ... N - 3, N - 2) 
  // and give them to f_impl through its template non-type parameter pack Is.
  f_impl(fn,
	 std::make_tuple(args...),
	 std::make_index_sequence<sizeof...(Args) - 1>());
}

template <typename Fn, typename Args,
	  std::size_t... Is>
void f_impl(Fn fn, Args args, std::index_sequence<Is...>)
{
  // Here Is... is a list (0, 1, 2, ..., N - 3, N - 2) of integers
  // Args is no longer a pack, but rather a single tuple of all the arguments
  // Now std::get<Is>(args)... is a comma-separated list of 
  // std::get<0>(args), std::get<1>(args), ..., std::get<N - 3>(args), std::get<N - 2>(args)
  // yielding all but the last element in the tuple.

  // The last element of the tuple is
  // std::get<std::tuple_size<Args>::value - 1>(args);

  // Call fn() with the first N - 1 elements
  fn(std::get<Is>(args)...);
}

This is called the indices trick.

Assuming C++14. Such little snippets have a strong tendency to improve drastically with more library support, and as the language revision advances.
Last edited on
@mbozzi: thx for this very interesting solution. However this code does throw some ugly template errors.
I did include
1
2
#include "tuple"
#include "utility" 

I cannot figure out what header is still missing or what is wrong. Template errors are so unreadable. Do you have an idea?
@JuliusCaesar,

I haven't check @mbozzi's code, but it may be that you haven't configured C++14 (or higher) support, and may be running the compiler as C++11.
Do you have an idea?

Not without seeing the code you're applying it to. Maybe a complete example will help:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# include <type_traits>
# include <tuple>
# include <cstdlib>
# include <cstdio>

template <typename Fn, typename Args,
	  std::size_t... Is>
void f_impl(Fn fn, Args args, std::index_sequence<Is...>)
{
  fn(std::get<Is>(args)...);
}

template <typename Fn, typename... Args>
void f(Fn fn, Args... args)
{
  f_impl(fn,
	 std::make_tuple(args...),
	 std::make_index_sequence<sizeof...(Args) - 1>());
}

void print (char const* a, char const* b)
{
  std::printf("%s, %s!\n", a, b);
}

int main()
{
  f(print, "hello", "world", 24);
}

http://coliru.stacked-crooked.com/a/5821c70b57de4911
https://rextester.com/KBOFW96875

The snippet is pretty naive as-is: it's not as general as it could be, and is inefficient besides. It's possible that some of those unneeded assumptions might be causing you problems.
Last edited on
Hey sorry,
problem was on my side. When i included the template file in a fresh translation unit everything worked fine. I will have to figure out which header files bite each other :)
But thx alot for your help !
Topic archived. No new replies allowed.