Variadic templates compiler error

May 31, 2019 at 10:47pm
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 May 31, 2019 at 10:48pm
Jun 1, 2019 at 12:30am
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.
Jun 1, 2019 at 2:03am
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 Jun 1, 2019 at 2:04am
Jun 1, 2019 at 10:13am
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.
Jun 1, 2019 at 7:11pm
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.
Jun 2, 2019 at 1:08am
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 Jun 2, 2019 at 1:14am
Jun 2, 2019 at 10:10pm
@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?
Jun 2, 2019 at 11:55pm
@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.
Jun 3, 2019 at 2:07am
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 Jun 3, 2019 at 4:08am
Jun 4, 2019 at 9:29pm
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.