tst
tst

Reputation: 1172

C++: Passing lambda pointer as a function pointer

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

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Aykhan Hagverdili
Aykhan Hagverdili

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

Related Questions