I am currently working on a library where I am chaining function objects.
I am creating a function template that takes a callable object (std::function at the moment) and is parametrized on the output and input type of the function. Here is a simplified version of what I am defining:
template <typename In, typename Out>
std::vector<Out> process(std::vector<In> vals, std::function< Out(In) > func)
{
// apply func for each value in vals
return result;
}
The problem I am having is on usage. It seems that when I pass a lambda, the compiler cannot deduce the type correctly, so complains that the function doesn't exist:
std::vector<string> strings;
// does NOT compile
auto chars = process(strings,
[]( std::string s ) -> char
{
return s[0]; // return first char
}
);
If I explicitly wrap the lambda in std::function, the program compiles:
std::vector<string> strings;
// DOES compile
auto chars = process(strings,
std::function< char(std::string) >(
[]( std::string s ) -> char
{
return s[0]; // return first char
})
);
I haven't tested passing function pointers or function objects yet, but it seems like it will be difficult for the compiler to deduce the the In and Out parameters if I'm not directly passing the explicit std::function object.
My question is this: is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
Perhaps parametrize the template on the function type instead of the input/return types? Essentially I need to deduce the In and Out type for an arbitrary callable. Perhaps some kind of auto/decltype trick for the return type of the template function?
Thank you.
I think what you can do is to create an intermediate return type deducing function which uses decltype to determine the arguments to be passed to the actual function object:
template <typename Out, typename In>
std::vector<Out> process_intern(std::vector<In> vals, std::function< Out(In) > func)
{
// whatever
}
template <typename In, typename Func>
auto process(std::vector<In> vals, Func func) -> std::vector<decltype(func(vals[0]))>
{
return process_intern<decltype(func(vals[0]))>(vals, func);
}
Of course, you may want to consider implementing the logic in process() directly anyway unless there is a reason to type erase the function type.
is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
No. User-defined conversions are not considered in template argument deduction. The compiler have to come up with In and Out such that the type of the parameter and the type of the argument have to match (almost) exactly, but they never will in this case.
Perhaps parametrize the template on the function type instead of the input/return types
Yes, that's what is normally done (take a look at the standard library algorithms, for example)
Related
This question already has answers here:
Why doesn't C++11 implicitly convert lambdas to std::function objects?
(3 answers)
Closed 1 year ago.
In c++17, I have a template function which takes some kind of lambda as input. However it only recognizes those with explicit types and ones using auto are rejected.
Why this is the case and any way to combine auto variables and template function taking lambda with specified form as input?
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
class C {};
vector<C> vec(2);
// ForEach func requires lambda must take Object with type C
// only, while its return type can vary as the function is templated.
template<typename T>
void ForEach(function<T (C a)>& lambda) {
std::for_each(begin(vec), end(vec), lambda);
};
int main()
{
auto
f_add_display4 = [](C i) {
};
std::function<void(C)>
f_add_display3 = f_add_display4;
ForEach(f_add_display3);
// ForEach(f_add_display4); // This line won't compile
}
A std function is not a lambda, and a lambda is not a std function.
Your function takes a std function. So when passed it, it deduces the template arguments to std function. If you pass a lambda, it cannot deduce anything.
Second, std function is a type erasure type, not an interface. Your function attempts to deduce the template argument of the type erasure class. Doing this is an anti pattern.
template<class F>
void ForEach(F lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
this works.
If you want to restruct the F to accepting some signature, in c++20 you can do:
void ForEach(std::invocable<C> auto lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
Or in c++17:
template<std::invocable<C> F>
void ForEach(F lambda) {
std::for_each(begin(vec), end(vec), lambda);
}
There are actually two fundamental issues here which cause the deduction to fail:
The first is that the type of a lambda expression is never std::function<Signature> for any signature Signature. However, your function expects a non-const reference argument. As the types differ a conversion is needed which would be a temporary object and temporary objects never bind to non-const reference. You could fix this issue by using a const reference as argument:
template <typename T>
void ForEach(function<T(C)> const& lambda) { ... }
The other problem is that ForEach takes a conceptually open-ended set of potential arguments. However, the argument you have isn't an immediate match: there is no way to to deduce the type T based on the lambda type to exactly match the function argument. Instead, a conversion is required. The compiler won't try to find what instantion might work as the target of an instantiation, although in this case there is only one choice. The conversion itself would work if you'd specify the target type (and made the previous choice of making the parameter const&:
ForEach<void>(f_add_display4);
I would recommend to not constraint the function to take a function<T(C)> to start with! Compared to using an actual lambda function is most likely a pessimization: while the lambda function is statically typed and can, in general, be optimized well, the same is not true for std::function<Signaure>. While the latter can sometimes be optimized often it isn't. If you want to constraint the function to only accept parameters of type C you can do that with some other approaches, probably involving SFINAE for C++17 compilers or a concept for C++20+.
That is, I'd recommend using
template <typename Fun>
void ForEach(Fun&& lambda) {
...
}
... of, if you want to constrain the function using C++20 concepts
template <typename Fun>
requires requires (Fun lambda, C c) { lambda(c); }
void ForEach(Fun&& lambda) {
...
}
The type of f_add_display3 is not std::function<void(C)>. It is of some unnamed class type (some class with a void operator()(C i)).
You can convert the lamda to a std::function<void(C)> as you do here:
std::function<void(C)> f_add_display3 = f_add_display4;
Though, template argument deduction does not consider implicit conversions.
If you change the argument to be a const reference, then you can specify the template argument explicitly:
template<typename T>
void ForEach(const function<T (C a)>& lambda) {
// ...
};
ForEach<void>(f_add_display4); // This line will compile !
Or you drop the unnecessary conversion to std::function entirely:
template <typename F>
void ForEach(F f) {
std::for_each(begin(vec), end(vec),f);
};
I have a function pointer that I need to pass to a function that expects a std::function. The function that takes the std::function is templated and uses the std::function's arguments to deduce a parameter pack, meaning an implicit conversion won't work.
I could construct the std::function myself, but the function being passed has many arguments, and writing them into the template brackets of std::function will not be sustainable, as I need to do this often with many similar functions that I pass to this function.
Is there a way to convert a function pointer to a std::function without specifying the return type and arguments, by some form of deducing?
This is what I've tried so far:
template <auto* F>
struct stdfunc {};
template <class Ret, class... Args, auto (*F)(Args...) -> Ret>
struct stdfunc<F>
{
typedef std::function<Ret(Args...)> type;
};
The above code does not work as intended. I found the syntax in this answer. In that case, it wasn't used for this purpose, but surely there is a way to achieve what I'm trying to do using this technique? It seems like all the pieces are there, I just have to put them in the right place.
Am I on the right track?
I suggest:
template<typename Func>
auto make_function(Func ptr)
{
return std::function<std::remove_pointer_t<Func>>(ptr);
}
and then simply pass "make_function(my_func)".
(Thanks toRemy Lebeau for suggesting use of "auto")
I have the following function
template <typename... Args>
void func(Args&&... args){
// do stuff
}
I now want to create a function pointer, that is callable with a variadic list of arguments, just like the function itself.
template <typename... Args>
using Var_Func = void(*)(Args... args)
At this point, I'm stuck. When creating a variable of type Var_Func, I have to give a list of types for the template:
Var_Func<[List of types]> var_func_p = &func;
This, however, prevents me from calling the pointer with a true variadic list of arguments. I want a pointer, that allows me to call it like a true variadic function, like this:
var_func_p("Test");
var_func_p(0.3, 5);
var_func_p('c', "Another test", 1.3, 3);
I have no idea how to achieve this. Can someone help me?
func isn't actually a function. It's a template that the compiler can use to create new functions when it needs to.
When you do func(5) the compiler creates a function called func<int> and then calls it. When you do func(5.0, 'a') the compiler creates a function called func<double, char> and then calls it. These are two completely different, unrelated functions with two different types.
You can create a pointer to func<int> or func<double, char> or func<> or func<anything else> in the usual way. But you cannot ever create a pointer to func, because func is not a function, it's an instruction for the compiler.
Simply put, this is not possible.
A function template is not a function, it's a template of a function -- and thus it does not have a function pointer until you specify which instantiation you want. At which point, the pointer is fixed to a specific number of arguments of specific types.
It is not possible in the C++ language to type-erase templates into a singular type, such as a pointer that you can pass around and lazily instantiate.
That is, code such as:
some_discrete_variadic_type x = var_func_1;
x(1);
x(1, "2");
x = var_func_2;
x(1);
x(1, "2");
Is not possible, because for type-erasure to work you must normalize the erasure to a fixed number of types (in this case, it would be instantiations).
Depending on what the problem is you are trying to solve, however, there might be workarounds -- though this would require more information.
If your uses are more limited -- such as to pass the functionality into other functions that are only ever known at compile-time, then you can use Functor objects instead and pass them into function templates where the arguments are deduced. For example:
struct var_func
{
template <typename...Args>
auto operator()(Args&&...args) -> void
{
// do something
}
};
Where an example of it being called could be:
template <typename T>
auto consume(T var_func_p) -> void
{
var_func_p("Test");
var_func_p(0.3, 5);
var_func_p('c', "Another test", 1.3, 3);
}
int main()
{
consume(var_func{});
consume(some_other_var_func{});
}
Note that in this case, you're not passing a function template around anymore. You're passing a static object, var_func, which contains a call-operator (operator()) that is a function template.
Like this:
template <typename... Args>
void func(Args&&... args){
// do stuff
}
template <typename... Args>
using f_ptr = void(*)(Args&&...);
int main() {
f_ptr<int,int> p = &func<int,int>;
p(3,1);
}
However, pointers to different instantations of func are incompatible.
This, however, prevents me to call the pointer with a true variadic list of arguments. I want a pointer, that allows me to call it like a true variadic function, like this:
You cannot store a pointer to eg func<double,double> in a f_ptr<int,int>.
I have the following template method:
template<typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args) {
auto func = std::bind(&T::template create<Window*, Args&...>, std::placeholders::_1, std::forward<Args>(args)...);
_store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
auto ret = func(window);
ret->set_name(name);
return ret;
});
}
Essentially all I need to do is bind the variadic Args to T::create (which itself is a static variadic template method), but allow populating the first argument (window) separately when it's called.
The above code fails with the following error
error: no match for call to ‘(const std::_Bind<std::shared_ptr<{anonymous}::SceneWithArgs> (*(std::_Placeholder<1>, const char*))(smlt::Window*&, const char (&)[4])>) (smlt::Window*&)’
auto ret = func(window);
~~~~^~~~~~~~
When calling the code like this:
manager.register_scene<SceneWithArgs>("test", "arg");
I don't really understand the error, or how to fix it.
I initially solved this by simply calling create inside the lambda, and this works on GCC 4.9 and above, but I have to remain compatible with GCC 4.8.4 and there's a bug which prevents using variadic args inside a lambda :(
UPDATE
OK so adding std::decay (as seen in the comments) didn't fix the problem entirely, the first argument kept deducing to Window*&& rather than Window*, but actually specifying the type of func (e.g. std::function<SceneBasePtr (Window*)>) instead of using auto made things compile.
I'm not sure why that is though...
You did not show the declaration of create, but I assume it looks as follows:
template <typename... Args>
static SceneBasePtr create(Args&&...);
It looks good. You use forwarding references, so that the compiler can deduce the exact types of arguments. As long as the compiler deduces the types, this will work. However...
&T::template create<Window*, Args&...>
Here, you instantiate the function template explicitly. That is, the compiler will no longer deduce the types, instead, it will replace the template parameters by the template arguments you provide. What are these template arguments?
template <typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args);
//...
manager.register_scene<SceneWithArgs>("test", "arg");
The first one is passed explicitly, it's Window*, this one is obvious. Now Args... -- "test" and "arg" are raw string literals, and their corresponding types are const char[5] and const char[4], respectively. Then Args... (after a deduction process) become const char(&)[5] and const char(&)[4]. Then what happens now, when you instantiate create with these types? You will end up with a declaration as below:
static SceneBasePtr create(Window*&&, const char(&)[5], const char(&)[4]);
Note that reference collapsing rules turn the first parameter into an r-value reference, while the rest are l-value references.
std::bind deduces types as well. However, std::bind will want to store the arguments, so that they can be reused later. The problem is that std::bind cannot store const char[5]. Instead, it will decay the type of each argument. This means that each raw string literal will become const char*, and will be passed as an l-value argument of this type to the bound function, and this does not match the manually instantiated create. A similar problem is with the window argument. It is deduced as an l-value, however, the manually instantiated create function expects an r-value.
It's best not to explicitly specify template types of a function template. If for some reasons (bug?) you cannot use args... inside the lambda, you can generate a proper create signature:
&T::template create<Window*&, typename std::decay<Args>::type&...>
// ~^~ ~~~~~~~~~^
To end up with:
static SceneBasePtr create(Window*&, const char*&, const char*&);
I have a template class with a callable type-parameter <typename Callable>.
I know that Callable indeed creates a callable object, and is often a lambda.
In my particular case, I also know the number (arity) and type of arguments (just one).
How can I get the return type of this callable type Callable on VS2010?
See std::result_of.
Pretending the object is invoked with one int argument, you could do things like:
using return_type = typename std::result_of<Callable(int)>::type;
This isn't generally possible.
There are multiple ways to have callable types, including lambdas and structs that overload operator().
C++ does not have nearly the type of reflection that languages like C# do, and it is impossible to do with the tools that C++ offers.
If all you want is to store the result of that "callable" into a variable, then you can just use auto.
If you actually want to do stuff with the result based on its type, then this question might help.
Basically, add this to your code.
template <typename T, typename U>
struct same_type
{
static const bool value = false;
};
template <typename T>
struct same_type< T, T >
{
static const bool value = true;
};
Then, if you have auto result = func(param);, where func is of type Callable, you can check the type of result with the following:
if (same_type<decltype(result), int>().value)
{
// code, knowing that result is of type int
}
else if (same_type<decltype(result), const char*>().value)
{
// code, knowing that result is of type const char*
}
// else if ... etc.
I tried various approaches, but support for C++11 in VS2010 is only partial and most approaches simply didn't compile.
What did finally work (on VS2010) is the following:
// When arity is known
typedef decltype(callbackInstance0()) return_type0;
typedef decltype(callbackInstance1(argInstance)) return_type1;
Where callbackInstanceX is the actual callable object to be used and argInstance is the actual arg to be passed to callbackInstance.
Note that this is not a general solution (though sufficient in my case) because:
It cannot be used outside of a function where you don't have actual instances of these types, but only the types, as in the class definition;
The callable arity must be known.