Reputation: 1172
I want to get values of an implicit function in C++. For simplicity let's say that I am interested in the function is x(a) which is defined by x*x - a == 0.
I have a root finder that uses Brent's method which is declared as
BrentsFindRoot( double (*f)(double), double a, double b, double tol )
Since the first argument is a pointer to a function double -> double, I thought I could accomplice that by using lambda functions. So I defined
auto Foo(double a) {
auto f = [a] (double x) noexcept -> double {
return x*x - a;
};
return f;
}
and I thought I could get sqrt(2)
by calling BrentsFindRoot(&Foo(2),0,10,1e-10)
. However, the compiler complains that it cannot cast a lambda function to a function pointer. I read that this can be done only if the lambda function does not capture anything, but in my case it needs to capture a
.
So what is the solution here? Is there any way to pass Foo(2)
to BrentsFindRoot
? Or alternatively how can I redeclare BrentsFindRoot
so that it accepts lambda functions?
I am using C++17
Upvotes: 0
Views: 397
Reputation: 275270
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.
Upvotes: 4
Reputation: 29955
Instead of a function pointer or std::function
, you can use a template too:
template<class Callback>
void BrentsFindRoot(Callback const& f, double a, double b, double tol ) {
// ...
}
Upvotes: 3