In this shortened example (not real world code), I'm attempting to call Callback with an int &, however, when going via the CallMethod method, the template parameter is interpreted as an int, meaning it can't convert it to the target parameter type.
Is this possible? I know I can cast the parameter to the correct type when calling CallMethod, however I'd like the solution to be implicit if possible.
#include <functional>
#include <iostream>
using namespace std;
void Callback(int &value)
{
value = 42;
}
template <typename Method, typename ...Params>
void CallMethod(Method method, Params ...params)
{
method(std::forward<Params>(params)...);
}
int main()
{
int value = 0;
CallMethod(&Callback, value);
cout << "Value: " << value << endl;
return 0;
}
You aren't correctly forwarding your arguments. In order to make use of perfect-forwarding std::forward should operate on forwarding references, which are when you have an rvalue reference in a deduced context. Your CallMethod function should look like this:
template <typename Method, typename ...Params>
void CallMethod(Method method, Params&& ...params)
{
method(std::forward<Params>(params)...);
}
Demo
Related
I am trying to count the number of arguments in the passed method signature of a template.
I want to know the number of arguments as this method is wrapped into a generic lamda and depending on the number of arguments passed, I will add a bit more information to the method passed in here.
I have isolated the issue to the snippit below. The snippit below works as expected but once I change the result method to take a reference of the METHOD, it fails compiling. Why s the reference on the paramter type influencing the type? How could I make it work with a reference?
Tested this with Microsoft Visual Studio 2017.
#include "pch.h"
#include <iostream>
template <typename Func>
struct func_traits;
template <typename R, typename... TArgs>
struct func_traits<R(*)(TArgs...)> {
static constexpr uint32_t ARG_COUNT = sizeof...(TArgs);
};
class TestClass {
public:
// Compile error!!!
// error C2027: use of undefined type 'func_traits<METHOD>'
// uint32_t Result(const METHOD& function)
template <typename METHOD>
uint32_t Result(const METHOD function) {
return (func_traits< METHOD >::ARG_COUNT);
}
};
void foo(int a, int b, int c)
{
}
int bar()
{
return 0;
}
int baz(double)
{
return 0;
}
int main()
{
TestClass device;
std::cout << device.Result(foo) << std::endl;
std::cout << device.Result(bar) << std::endl;
std::cout << device.Result(baz) << std::endl;
return 0;
}
The scenario in your example is very rare and is related to particular template rule related specifically to functions and array.
To elaborate if you have a template function which takes another function by reference then type deduced for T is actual function type (not pointer to function).
For e.g.
template<typename T>
void fun(const T& f);
{
.....
}
void print(int);
fun(print); //for this case T will be deduced as void(int) and not void(*)int i.e. T will not be a function pointer
So to get your example working for reference case you have to create one more template specialization like :
template <typename R, typename... TArgs>
struct func_traits<R(TArgs...)> {
static constexpr uint32_t ARG_COUNT = sizeof...(TArgs);
}; //note (*) is removed from template function
In short if you are passing function or array by value then they are decayed to pointer but if you are passing by reference then they are treated as it is.
More details are available in Scott Meyers Effective Modern C++ (https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html)
I want to define a template function that gets one argument passed by value for all types but std::string (and const char*).
template<typename T>
void foo( T value )
{
// some code using value
}
The std::string version should behave exactly as the template version, but have its parameter passed by const&.
What is the best approach to do what I want without duplicating the body of foo()?
The best I was able to think is to wrap the code using value inside another function, and then call it inside all versions of foo() (the template version and the std::string overload). Is there another way? For example, is it possible to call the template version from within the std::string overload?
EDIT
What I want to know is a good rule of thumb for avoiding code duplication among various specializations and overloads. What is a good pattern to follow? Shall I define a wrapper function for the body and then call that from within all overloads/specializations, or there is another way?
In order to avoid code duplication, the answer by 101010 can be extended to actually call the template from within the overload:
#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>
template<typename T>
void foo( T value )
{
std::cout << "inside template" << std::endl;
std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}
void foo(const std::string &value)
{
std::cout << "inside const string overload" << std::endl;
foo<const std::string&>(value);
}
int main()
{
foo(10);
foo(std::string("hello"));
return 0;
}
output
inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >
live example
Simple solution: provide an overload for std::string:
void foo( std::string const &value ) {
// some code using value
}
I think what you are looking for is rvalue signature in C++ 11.
Its as simple as:
#include <iostream>
#include <string>
template<typename T>
void foo(T&& value)
{
std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;
std::cout << value << std::endl;
}
int main()
{
std::string text = "hello";
foo(text);
foo(1);
}
You can either pass the parameter by reference or by value and the rvalue rules will use the appropriate type.
You can define a type-trait-like class that will convert std::string to std::string& and will keep the type for all other types:
template<class T>
struct helper {
typedef T type;
};
template<>
struct helper<std::string> {
typedef std::string& type; // or const std::string& type if you want
};
template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
value = value2;
}
int main()
{
int a = 10;
foo(a, 42);
std::cout << a << std::endl; // prints 10
std::string s = "abc";
foo(s, std::string("def"));
std::cout << s << std::endl; // prints def
}
Full example: http://coliru.stacked-crooked.com/a/96cf78e6c4846172
UPD: as noted by #PiotrSkotnicki, having only one parameter makes type-deduction fail. However, I will keep the answer as it might be helpful in case you indeed have several parameters of type T or if you are ok with specifying explicit template parameter to foo.
UPD2: To solve the type-deduction problem, you may add another wrapper:
template<typename T>
void foo_helper( typename helper<T>::type value )
{
value = T();
}
template<typename T>
void foo(T& value)
{
foo_helper<T>(value);
}
This still might have some problems, so whether this is applicable to your usecase, is up to you to decide.
use std::enable_if + std::is_convertibale:
template<typename T>
typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
{
// some code using value
}
EDIT: Just to clarify "t" is successfully called when casted. The compiler knows and does state that it is a function pointer that takes an argument of type int. I supply a null int pointer to break the loop because it is calling itself recursively. It may just be a bug in the compiler.
I am trying to call a function from a template function argument.
I would assume that it would be possible to call the function without explicit casting but that does not seem to be the case. Using VC2013.
template<typename T>
void func(T t)
{
printf("calling func...\n");
if (t)
{
((void(__cdecl*)(int))t)((int)nullptr); // explicit casting is successful
t ((int)nullptr); // compile error: ``term does not evaluate to a function taking 1 arguments``
}
}
void main()
{
auto pe = func < int > ;
auto pf = func < void(__cdecl*)(int) >;
pf(pe);
}
You have the error for func<int> which becomes:
void func(int t)
{
printf("calling func...\n");
if (t)
{
((void(__cdecl*)(int))t)((int)nullptr); // bad casting
t ((int)nullptr); // compile error: int is not a callable object
}
}
When t is an int, of course you can't treat it like a function. You'll have to specialize the template for ints or use a different function. Also, please forget that there are C-style casts, they only serve to shoot yourself into the foot.
I don't understand what do you want exactly. But maybe something like this ?:
#include <iostream>
#include <type_traits>
template<typename T>
void call_helper(T value, std::true_type) // value is function
{
std::cout << "Function" << std::endl;
value(0);
}
template<typename T>
void call_helper(T value, std::false_type) // value is NOT function
{
std::cout << "Not function" << std::endl;
std::cout << value << std::endl;
}
template<typename T>
void call(T value)
{
call_helper(value, std::is_function<typename std::remove_pointer<T>::type>());
}
int main()
{
void (*f)(int) = call<int>;
call(f);
}
live example: http://rextester.com/DIYYZ43213
I am attempting to recreate the Observer pattern where I can perfectly forward parameters to a given member function of the observers.
If I attempt to pass the address of a member function which has multiple overrides, it cannot deduce the correct member function based on the arguments.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Class>
struct observer_list
{
template<typename Ret, typename... Args, typename... UArgs>
void call(Ret (Class::*func)(Args...), UArgs&&... args)
{
for (auto obj : _observers)
{
(obj->*func)(std::forward<UArgs>(args)...);
}
}
std::vector<Class*> _observers;
};
struct foo
{
void func(const std::string& s)
{
std::cout << this << ": " << s << std::endl;
}
void func(const double d)
{
std::cout << this << ": " << d << std::endl;
}
};
int main()
{
observer_list<foo> l;
foo f1, f2;
l._observers = { &f1, &f2 };
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
return 0;
}
This fails to compile with template argument deduction/substitution failed.
Note that I had Args... and UArgs... because I need to be able to pass parameters which are not necessarily the same type asthe type of the function signature, but are convertible to said type.
I was thinking I could use a std::enable_if<std::is_convertible<Args, UArgs>> call to disambiguate, but I don't believe I can do this with a variadic template parameter pack?
How can I get the template argument deduction to work here?
The issue is here:
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
For both lines, the compiler doesn't know which foo::func you are referring to. Hence, you have to disambiguate yourself by providing the type information that is missing (i.e., the type of foo:func) through casts:
l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double )>(&foo::func), 0.5);
Alternatively, you can provide the template arguments that the compiler cannot deduce and that define the type of func:
l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double >(&foo::func, 0.5);
Notice that you have to use double and not const double above. The reason is that generally double and const double are two different types. However, there's one situation where double and const double are considered as if they were the same type: as function arguments. For instance,
void bar(const double);
void bar(double);
are not two different overloads but are actually the same function.
The following template definition
template <typename Func, typename ReturnType, typename... Arguments>
class Command
{
public:
Command(Func f) : m_func(f) { }
ReturnType operator()(Arguments... funcArgs) { return m_func(funcArgs...); }
private:
Func m_func;
};
gives an error message with gcc 4.7.3 (error: field 'Command::m_func' invalidly declared function type) when instantiated with the following test code:
void testFunction(int i, double d)
{
std::cout << "TestFunctor::operator()(" << i << ", " << d << ") called." << std::endl;
}
int main()
{
void (&fRef)(int, double) = TestFunction;
Command<void(int, double), void, int, double> testCommand(fRef);
}
The error message also occurs if I pass TestFunction without the address-of operator into the testCommand constructor, but disappears if I pass either an explicitly named function pointer or use the address-of operator to pass the parameter. I'm under the impression that this code should work given Chapter 5 of Modern C++ Design.
What is the reasoning behind not being able to store a reference to a function, but function pointers work fine? Are there any workarounds that would allow this to compile without losing support for being able to pass functors as arguments to Command's constructor as well?
Changing one line could fix it:
Command<void(*)(int, double), void, int, double> testCommand(fRef);
The difference is, you're passing a function pointer now, instead of a function type. (Functions aren't copyable, but pointers are).
The reference fRef decays to a function pointer when you pass it.
I wouldn't suggest using std::function if performance mattered.
See it live on Coliru
Note that with a little rewriting, you can make it all work much nicer:
int main()
{
auto command = make_command(testFunction);
command(1, 3.14);
}
To do this, I'd suggest changing the Command template to be:
template <typename Func>
class Command
{
Func m_func;
public:
Command(Func f) : m_func(f) { }
template <typename... A> auto operator()(A... args) const
-> decltype(m_func(args...))
{ return m_func(args...); }
};
And now you can have type-deduction on the Func template parameter by having a factory function:
template <typename Func> Command<Func> make_command(Func f)
{
return Command<Func>(f);
}
See this approach live on Coliru too. Of course, the output it the same:
TestFunctor::operator()(1, 3.14) called.
C++11 offers an std::function template. You don't have to mess with function pointers.
You can pass those by reference, copy them, move them and they can even be used to store lambdas:
std::function<void()> func = []() { std::cout << "Hi" << std::endl; };