0

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

2 Answers 2

4

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.

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

2 Comments

maybe also worth mentioning that the lambda is not a function, but an object with an operator(), and only considering that passing a pointer to the lambda was the wrong path to take
@4603 If the C API was the one above, you'd end up passing a pointer to lambda plus a wrapping lambda for f. I am not that interested in teaching C++ type names and terms, as useful as it is to know.
3

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 ) {
    // ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.