C++ Lambda With Captures as a Function Pointer

C++ lambda with captures as a function pointer

Since capturing lambdas need to preserve a state, there isn't really a simple "workaround", since they are not just ordinary functions. The point about a function pointer is that it points to a single, global function, and this information has no room for a state.

The closest workaround (that essentially discards the statefulness) is to provide some type of global variable which is accessed from your lambda/function. For example, you could make a traditional functor object and give it a static member function which refers to some unique (global/static) instance.

But that's sort of defeating the entire purpose of capturing lambdas.

Return lambda with capture from a function in c++11

std::function<void()> is not a function pointer. std::function<void()> can store more than just function pointers.

If you'd try to return a function pointer void(*)() then the code would fail to compile, because lambdas with capture do not have a conversion to function pointer.

As parameter is passed and capture by referene and var is still in scope while you call foo() the code is fine.

Passing capturing lambda as function pointer

A lambda can only be converted to a function pointer if it does not capture, from the draft C++11 standard section 5.1.2 [expr.prim.lambda] says (emphasis mine):

The closure type for a lambda-expression with no lambda-capture has a
public non-virtual non-explicit const conversion function to pointer
to function
having the same parameter and return types as the closure
type’s function call operator. The value returned by this conversion
function shall be the address of a function that, when invoked, has
the same effect as invoking the closure type’s function call operator.

Note, cppreference also covers this in their section on Lambda functions.

So the following alternatives would work:

typedef bool(*DecisionFn)(int);

Decide greaterThanThree{ []( int x ){ return x > 3; } };

and so would this:

typedef bool(*DecisionFn)();

Decide greaterThanThree{ [](){ return true ; } };

and as 5gon12eder points out, you can also use std::function, but note that std::function is heavy weight, so it is not a cost-less trade-off.

C++: Passing lambda pointer as a function pointer

Your BrentsFindRoot takes a stateless function pointer.

Your lambda has state.

These are not compatible. Both conceptually and syntactically.

BrentsFindRoot( double (*f)(void const*, double), void const*, double a, double b, double tol )

this is how the signature would change if you added state and wanted it to remain a pure C function. Then passing a lambda works conceptually, but the syntax is awkward. If you do not mind C++ in your root finder:

BrentsFindRoot( std::function<double(double)> f, double a, double b, double tol )

Alternatively, you can shoe-horn state into a stateless function pointer via table/global state tricks. You could also make the lambda stateless by taking and storing something equivalent to a as a compile time parameter.

But just do the std::function version.

if BrentsFindRoot is a header only function, you can use a template

template<class F>
void BrentsFindRoot( F f, double, double, double );

a final option is to find or write a function_view type; this can be more efficient than a std::function by avoiding storage.

union function_state {
void* pvoid;
void(* pfvoid)();
function_state(void* p=nullptr):pvoid(p) {}
template<class R, class...Args>
function_state(R(*pf)(Args...)):pfvoid(reinterpret_cast<void(*)()>(pf)) {}
};
template<class Sig>
struct function_view;
template<class R, class...Args>
struct function_view<R(Args...)> {
function_state state;
R(*pf)(function_state, Args&&...args) = nullptr;

R operator()(Args...args)const {
return pf(state, std::forward<Args>(args)...);
}
function_view(function_view const&)=default;
function_view& operator=(function_view const&)=default;
explicit operator bool() const{ return pf; }

function_view( R(*f)(Args...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R(*)(Args...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}


template<class F, std::convertible_to<R> FR=std::invoke_result_t< F, Args... >>
requires (!std::is_same_v<R,void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->R{
return (*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}
template<class F>
requires (std::is_same_v<R, void>)
function_view( F&& f ):
state((void*)std::addressof(f)),
pf([](function_state s, Args&&...args)->void{
(*static_cast<F*>(s.pvoid))( std::forward<Args>(args)... );
})
{}


template<std::convertible_to<R> R0, std::constructible_from<Args>...As>
requires (!std::is_same_v<R,void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->R{
return reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
template<class R0, std::constructible_from<Args>...As>
requires (std::is_same_v<R, void>)
function_view( R0(*f)(As...) ):
state(f),
pf([](function_state s, Args&&...args)->void{
reinterpret_cast<R0(*)(As...)>(s.pfvoid)( std::forward<Args>(args)... );
})
{}
};

but that probably isn't something you want to write quite yet.

how to define function pointer compatible with using lambda with capture as call back

You might use std::function to allow capturing lambdas and other functors and regular function pointers:

void(*funcDX)(CDataExchange* pDX);
void(*funcDXpDoc)(CDataExchange* pDX, CDocument *pDoc);
void(*funcInit)(CWnd *dlgWnd);
void(*funcOK)(CWnd *dlgWnd, CDocument *pDoc);

becomes

std::function<void(CDataExchange*)> funcDX;
std::function<void(CDataExchange*, CDocument*)> funcDXpDoc;
std::function<void(CWnd*)> funcInit;
std::function<void(CWnd*, CDocument*)> funcOK;

You setter/constructor should change pointer function to std::function too:

void SetInitCallBack(std::function<void(CWnd*)> f) { funcInit = f; }

Else your usage is identical:

if (funcDX) funcDX(pDX);

or

funcInit = nullptr;

Copying c++ lambda to Function Pointer reference

Is the behaviour the same as If I would just define an ordinary function or do I have any side effects here?

Yes, it's the same. A captureless lambda is convertible to a regular function pointer because, to quote the C++ standard ([expr.prim.lambda.closure]/6, emphasis mine):

The closure type for a non-generic lambda-expression with no
lambda-capture has a conversion function to pointer to function with
C++ language linkage having the same parameter and return types as the
closure type's function call operator. The conversion is to “pointer
to noexcept function” if the function call operator has a non-throwing
exception specification. The value returned by this conversion
function is the address of a function F that, when invoked, has the
same effect as invoking the closure type's function call operator.

So while the lambda goes out of scope, that pointer is backed by a proper function, just as if you had written it yourself at file scope. Functions "live" throughout the entire execution of the program, so the pointer will be valid, always.

I can't pass lambda with reference capture

You can only do the above with capture-less lambdas.

See [expr.prim.lambda.closure] (sec 7)

The closure type for a non-generic lambda-expression with no
lambda-capture
whose constraints (if any) are satisfied has a
conversion function to pointer to function
with C++ language linkage
having the same parameter and return types as the closure type's
function call operator.

Since lambdas are not just ordinary functions and capturing it need to preserve a state,
you can not find any simple or conventional solution to make them assign to function pointers.


To fix, you can use std::function which will do it by type erasure:

#include <functional> // std::function

int test;
std::function<float(int)> f = [&](int i) -> float {return static_cast<float>(test); };

C++11 lambdas to Function Pointer

I can not use the STL std::function (because my program does not use C++ RTTI and EXCEPTIONS runtime)

Then you may need to write your own equivalent to std::function.

The usual implementation of type erasure for std::function doesn't need RTTI for most of its functionality; it works through regular virtual function calls. So writing your own version is doable.

Indeed, the only things in std::function that need RTTI are the target_type and target functions, which are not the most useful functions in the world. You might be able to just use std::function without calling these functions, assuming that the implementation you're using doesn't need RTTI for its usual business.

Typically, when you disable exception handling, the program simply shuts down and errors out when encountering a throw statement. And since most of the exceptions that a std::function would emit aren't the kind of thing you would be able to recover from (calling an empty function, running out of memory, etc), you can probably just use std::function as is.

Why cpp std::function can hold capture-lambda, while function pointer cannot?

Why can a function pointer not hold a lambda with a capture : because a Lambda is NOT a function ,it's an object!

Why can a lambda without a capture be converted to a function pointer ?

A Lambda is just an ordinairy object (a piece of data) of a compiler generated class (with a unique classname that only the compiler knows) with a function-operator member (i.e. auto operator() ( ??? )) that the compiler defines for you with the parameter definitions (if any) you provide. The data-members of a lambda-object are defined by the capture-list and/or usage of variables of its enclosing scope.

All non-static member functions when called on an object get a implicit hidden argument called this. This is also the case when you 'call' the lambda.

Now ,when you don't capture something ,the lambda has no data (empty class) and the compiler doesn't have to generate an implicit this pointer for the call ,which makes the function operator just like an ordinairy function and the compiler can convert it to function pointer.
So it not the lambda that is converted to a function-pointer ,it's the lambda's function-operator that is converted.

Why can std::function hold both : because it's a template and with templates and specializations you can do almost anything.



Related Topics



Leave a reply



Submit