1

The following code does not compile:

typedef void(*RunnableFun)(int);  //pointer-to-function type

void foo(RunnableFun f) {
}
void bar(const std::string& a) {
    foo([&](int) -> void { std::cout << a; });
}

and IntelliSense is telling me

no suitable conversion function from "lambda []void (int)->void" to "RunnableFun" exists

and the compiler is complaining

  'void foo(RunnableFun)' : cannot convert argument 1 from 'bar::<lambda_796873cf40a6be4e411eb9df14f486bf>' to 'RunnableFun'

But the following does compile:

typedef void(*RunnableFun)(int);  //pointer-to-function type

void foo(RunnableFun f) {

}
void bar(const std::string&) {
    // Notice the empty capture list
    foo([](int) -> void { std::cout << "dummy"; });
}

How can I keep the signature of foo() but achieve what I tried in to in the first code example?

P.S.: Changing foo's signature to void foo(std::function<void(int)> f) would compile but can I do it without changing it?

0

2 Answers 2

7

You could take a look at most of the standard library algorithm functions. When they take a "predicate" (callable object) they do it as a template arguments.

So you could make your function as a template:

template<typename FunctionType>
void foo(FunctionType f) {
}

No other change is needed.

It's either this or using std::function, you can't solve your problem without changing the foo function.

Sign up to request clarification or add additional context in comments.

Comments

3

A pointer to function only stores a location of execution, it stores no additional state. The only change between executions must be determined by global state.

In your case, you want the lambda to capture local state. So when called after a="hello" it prints a different value than when called after a="world".

The short answer is "Too bad, so sad, but no".

You can hack a bit. You could store a in a static std::string ga; and accessmit within the lambda. Note that this is ugly, not reentrant, exposes your code to global state needlessly, and generally a bad idea.

void bar(const std::string& a) {
  static std::string ga;
  ga = a; // note, separate line
  foo([](int) -> void { std::cout << ga; });
}

Generally pure function pointer callback APIs are a sign an ignorant fool designed the API: a proper C-style callback takes a void*, and a proper C++ callback is a std::function or template callable or similar. (Ignorant as they are not aware that the void* pattern is common: fool because even if you did not know of it, they did not work it out themselves after trying their system a few times and noticed the big gaping hole. It is ok: most programmers are fools until they make all the mistakes themselves!)

If you actually have a void* or equivalent argument you get called back with, you omitted it from your question, and the answer is entirely different. Just store a ptr-to-lambda in that void*, and store a stateless lambda that casts-and-calls the stateful lambda from the void*.

4 Comments

"that casts-and-calls the stateful lambda from the void*" I'm curious as to how exactly that would happen. After all, the type of a lambda is not known, so you'd need to use decltype(some_variable) to get it and perform the cast. So where do you get the variable? I think it'd be better to wrap the stateful lambda in a std::function and pass that as the void* in question. That way, you know exactly what type to cast it to.
@nicol auto f=[&](int x){std::cout<<a<<'\n';}; foo( [](void* ptr, int x){ auto* pf = static_cast<decltype(f)*>(ptr); (*pf)(x); }, &f );? Or wrap the lambda-and-void* pattern into a class with a factory function that deduces the type of F? Nothing fundamentally hard here, and you need to store the lambda somewhere, so decltype has a natural target (wherever it is).
Interesting hack, albeit not applicable for my multithreaded application. Marked as answer because of its many details
@phillab in multi threaded, thread local storage can be used.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.