C-style wrapper to C++ member function, ordinary function pointer to member function

126 views Asked by At

So I sort of tried to accomplish “the impossible” at least in C++20 and with the constraint that the class for which I am extracting a member function pointer can be evaluated as a constexpr.

#include <iostream>

struct AddFunctor
{
    double val;
    double operator()(double x) const { return val + x; }
};

struct SubFunctor
{
    double val;
    double operator()(double x) const { return val - x; }
};

typedef double(*DblFn)(double);  // C-style type of above operator()

// the below is doable only in C++20 which is more liberal about NTTP-s
template<typename FunctorType, FunctorType functor>
double functorWrapper(double x) { return functor(x); }

// below wrapper doesn't work, see below
template<typename FunctorType>
DblFn makeWrapper(FunctorType fr) { return &(functorWrapper<FunctorType, fr>); }

int main()
{
    DblFn fp;
    
    // if constexpr below is changed to const, one gets:
    // error: assigning to 'DblFn' (aka 'double (*)(double)') from incompatible type '<overloaded function type>'
    constexpr AddFunctor af{2};
    constexpr SubFunctor sf{3};

    // fp = &(af.operator());  // error: cannot create a non-constant pointer to member function

    fp = &(functorWrapper<AddFunctor, af>);
    std::cout << fp(6.283) << std::endl;

    fp = &(functorWrapper<SubFunctor, sf>);
    std::cout << fp(6.283) << std::endl;

// if the below "if 0" block is uncommented, one gets:
/*
<src>:21:46: error: address of overloaded function 'functorWrapper' does not match required type 'double (double)'
DblFn makeWrapper(FunctorType fr) { return &(functorWrapper<FunctorType, fr>); }
                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<src>:40:10: note: in instantiation of function template specialization 'makeWrapper<AddFunctor>' requested here
    fp = makeWrapper(af);
        ^
<src>:18:8: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'functor'
double functorWrapper(double x) { return functor(x); }
    ^
1 error generated.
*/
#if 0
    fp = makeWrapper(af);
    std::cout << fp(2.718) << std::endl;
#endif
}

As I am not C++-savvy enough, I ask this question: is it possible to write a makeWrapper function as I have tried to do without manually specifying the FunctorType and find it out using template argument type deduction, and if yes how?

PS: My library is implemented in C++ for it's expressive power but the interface is basically C for FFI flexibility with ifdef __cplusplus function goodies available. Hence I'm specifically looking for solutions for my C++ clients to convert to C-style pointers to maintain a common ABI.

1

There are 1 answers

1
JeJo On

If you can use std::function, this is rather a simple case of wrapping the class::operator() into a lambda function.

#include <functional>

using DblFn = std::function<double(double)>;

template<typename FunctorType>
constexpr auto makeWrapper(FunctorType functor)
{
   // Define the lambda directly as a function pointer
   return [=](double x) { return functor(x); };
}

Now you can

AddFunctor af{ 2 };
const SubFunctor sf{ 3 };
constexpr SubFunctor xf{ -8 };

DblFn fp = makeWrapper(af);
std::cout << fp(6.283) << std::endl;   // prints: 8.283

fp = makeWrapper(sf);
std::cout << fp(6.283) << std::endl;   // prints: -3.283


fp = makeWrapper(xf);
std::cout << fp(6.283) << std::endl;   // prints: -14.283

Live demo

The c-style function pointers require the type of the member functions to be accurate (i.e. const, non const, noexcept etc.); hence the issue! On the other hand std::function helps you with its type erasure power to capture, the wrapper (stateful) lambda and achieve the goal.