Can't pass std::function as a parameter because there is no suitable conversion.

Hello,

I have a function that takes function pointer as a parameter. Sometimes I would like to pass lamba instead of function pointer. However, my lambda captures so it can't be converted to function pointer.

So I was looking for solution, and I saw suggestion that I could store my lambda in std::function (I know that std::function is slow and I am fine with it).

So I've tried to do that, but I am getting compilation error. Here is my example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <functional>
#include <vector>

using FrameCallback = void(*)(const std::string &error_message, bool success);

void DoSomething(FrameCallback callback)
{
  callback("hello", true);
}

int main()
{
  std::vector<std::string> strings;

  std::function<void(const std::string&, bool)> callback = [&](const std::string &error_message, bool success)
  {
	strings.push_back(error_message);
  };

  DoSomething(callback); //error C2664: 'void DoSomething(FrameCallback)': cannot convert argument 1 from 'std::function<void (std::string &,bool)>' to 'FrameCallback'

}


Thanks for any help.
Last edited on
You must change so that DoSomething also uses std::function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <functional>
#include <vector>

using FrameCallback = std::function<void(const std::string& /*error_message*/, bool /*success*/)>;

void DoSomething(FrameCallback callback)
{
	callback("hello", true);
}

int main()
{
	std::vector<std::string> strings;

	FrameCallback callback = [&](const std::string &error_message, bool success)
	{
		strings.push_back(error_message);
	};

	DoSomething(callback);
}
Last edited on
That I don't want to do. Is there any other way? I don't need to use std::function.
I just want to construct something locally and pass it to DoSomething as a function pointer.
What about using a template parameter as callback?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<class Callback>
void DoSomething(Callback callback)
{
  callback("hello", true);
}

int main()
{
  std::vector<std::string> strings;

  auto cb = [&](const std::string &error_message, bool success)
  {
    strings.push_back(error_message);
  };

  DoSomething(cb); 
}

You can use a lambda, functor or ordinary function
Well, you cannot convert a std::function to a function pointer for obvious reasons.

If DoSomething doesn't store the callback and you are only worried about the extra overhead of using std::function you might want to consider turning DoSomething into a function template where the type of the callback is deduced from the argument. This is how many of the standard algorithms work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <functional>
#include <vector>

template <typename FrameCallback>
void DoSomething(FrameCallback callback)
{
	callback("hello", true);
}

int main()
{
	std::vector<std::string> strings;

	auto callback = [&](const std::string &error_message, bool success)
	{
		strings.push_back(error_message);
	};

	DoSomething(callback);
}
Thank you, I don't want to use template parameter, because that requires to implement details of my function in a header and I hate it :/
Hi Peter.

I got notification about your response in which you said:

Well, in that case I think you are out of luck. I guess you could use the C approach and pass a function pointer + a void* to the data that the function is responsible to interpret and use correctly. This is neither type-safe nor fun to work with so I wouldn't recommend going down that road, but it's the only way that I know to satisfy your requirement of using function pointers and no templates.


but it haven't shown up here. Strange...

I think there might be a way, take a look at this example (this is almost a cut and paste from https://en.cppreference.com/w/cpp/utility/functional/function/target

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
30
31
32
33
34
35
36
37
38
#include <functional>
#include <vector>
#include <iostream>

using FrameCallback = int(*)(int, int);

void DoSomething(FrameCallback callback)
{
  callback(1,2);
}

int f(int, int) { return 1; }
int g(int, int) { return 2; }
void test(std::function<int(int, int)> const& arg)
{
  std::cout << "test function: ";
  if (arg.target<std::plus<int>>())
	std::cout << "it is plus\n";
  if (arg.target<std::minus<int>>())
	std::cout << "it is minus\n";

  int(*const* ptr)(int, int) = arg.target<int(*)(int, int)>();
  if (ptr && *ptr == f)
	std::cout << "it is the function f\n";
  if (ptr && *ptr == g)
	std::cout << "it is the function g\n";

  if (ptr)
	DoSomething(*ptr); // This is what I've added and it works!
}

int main()
{
  test(std::function<int(int, int)>(std::plus<int>()));
  test(std::function<int(int, int)>(std::minus<int>()));
  test(std::function<int(int, int)>(f));
  test(std::function<int(int, int)>(g));
}


You see what I did in this line?

1
2
  if (ptr)
	DoSomething(*ptr);


Now I need to try to convert it to my original example. So far I've failed, but will keep trying :)
I got notification about your response in which you said:
[...]
but it haven't shown up here. Strange...

I decided to remove that post because I realized it probably wasn't helpful.

DoSomething(*ptr); // This is what I've added and it works!


Note that DoSomething is only called for f and g. If the std::function does not hold a function pointer (or something that can be converted to a function pointer) there is no way you can extract a function pointer from it.

It's because std::function can be implicitly constructed from a function pointer, so if your function takes a std::function as argument (as showed in my first reply) you can pass a function pointer, a lambda closure, or a std::function object, without having to do any explicit conversions.

Not sure why the cppreference example is so verbose. The main function could be simplified as follows.

1
2
3
4
5
6
7
int main()
{
	test(std::plus<int>());
	test(std::minus<int>());
	test(f);
	test(g);
}
Last edited on
Note that DoSomething is only called for f and g. If the std::function does not hold a function pointer (or something that can be converted to a function pointer) there is no way you can extract a function pointer from it.


Yes, but that is actually cool feature and sorf of type safety you've mentioned earlier.

if your function takes a std::function as argument (as showed in my first reply) you can pass a function pointer, a lambda closure, or a std::function object, without having to do any explicit conversions.

Ah, now I get it. I thought that I have to construct std::function if my method expects parameter of type std::function. Of coure, you have shown example with lambda but I just did not read it properly.

Does method with std::function parameter performs slower than method with function pointer? Even when I am not passing std::function to it? I guess it has to be slower because function pointer is implicitly converted to std::function.
Does method with std::function parameter performs slower than method with function pointer? Even when I am not passing std::function to it? I guess it has to be slower because function pointer is implicitly converted to std::function.

Yes, that is likely the case. How much of a difference it makes depends though. If it's a large function that is only called rarely it wouldn't matter. If it's a small function that is called a lot (e.g. a comparator for std::sort) then it might make the code significantly slower (perhaps 2-3x).
Thank you.

Would you be able to help me with this case?

test(std::function<int(int, int)>([](int a, int b)->int {return a + b; }));

For above case ptr == nullptr so I guess lambda type is different than type passed as template parameter.

This code shows that it is true:
1
2
  std::cout << '\n' << "Lambda type: " << typeid(my_lambda).name() << '\n';
  std::cout << "g type: " << typeid(g).name() << '\n';


because I get:

1
2
Lambda type: class <lambda_2b4822f6797d23008e42149581bc7ffd>
g type: int __cdecl(int,int)


So obviously these types are different. Can I force lambda to be the same type of (int*)(int,int) ?

Remember that in my original use case lambda captures that is why I can't use implicit conversion to function pointer.
Peter, I got it working:

1
2
3
4
5
  int c = 7;
  auto my_lambda = [&](int a, int b)->int {return a + b + c; };
  static auto static_lambda = my_lambda;
  int(*ptr)(int, int) = [](int a, int b) { return static_lambda(a, b); };
  test(std::function<int(int, int)>(ptr));


So the idea is to wrap capturing lambda in capturless one :)
I've learned a lot, thank you.
If you are using std::function you shouldn't have to care about this stuff. I didn't even know about the target function before you showed me.

It is true that each lambda has its own type. If the lambda doesn't capture anything it can be implicitly converted to a function pointer, but that doesn't happen when you pass it to the std::function constructor because it accepts the lambda type just fine so no implicit conversion is necessary.

If you absolutely must store a function pointer inside the std::function object you could use an explicit cast ...
 
test(static_cast<int(*)(int, int)>([](int a, int b)->int {return a + b; }));
... but as I said, I don't think this should be necessary.
So the idea is to wrap capturing lambda in capturless one :)

You cannot do that.

A function pointer is just a pointer to some piece of code.

You can think of a lambda as creating an object that contains a function pointer plus everything that has been captured. A lambda that does not capture anything can be implicitly converted to a function pointer. This should be fine because the code that the function pointer refers to (i.e. the lambda body) does not use anything within the object (because it's empty).

If you were able to convert a capturing lambda into a function pointer you would lose everything that had been captured. The situation is similar to that of pointers to member functions. A pointer to a member function is useless unless you have an object to call it on.

What you are essentially saying is that you want to capture a lambda from a capturless lambda, but that doesn't make sense, because if it captures it's no longer capturless.

Update: After reading more carefully that is actually not what you are saying. You can indeed use global and static variables from lambdas without capturing them but you need to be a bit careful with that. Your latest example above would not work if that piece of code was executed more than once because the static_lambda would contain a dangling reference to an old version of c that no longer exist.
Last edited on
Topic archived. No new replies allowed.