Search code examples
c++pointersfunction-pointerspointer-to-member

How to legally cast function pointer to method pointer?


I'm looking for the opposite of std::mem_fn: turning a function pointer into a member pointer (e.g. void(*)(C*) to void(C::*)() for a given class C).

Background

I'm using a 3rd party library (say lib1.h) to create bindings for another 3rd party library (lib2.h).

Some functions in lib1.h receive method pointers to methods that accept a Value argument (also defined in lib1.h). In other hand, lib2.h classes do not contain such methods, so I have to do the wrappings manually through a lambda function:

/* lib1.h (can't touch this) */ 
class Value;
class Property;

template <typename C>
struct ObjectWrap {
  using Method = void(C::*)(Value);
  static Property declare(std::string name, Method method); 
};

/* lib2.h (can't touch this) */ 
struct Person {
  void greet(std::string name);
};

/* bindings.cpp */
std::string value_to_std_string(Value v);

Property declarePersonGreet() {
  return ObjectWrap<Person>::declare("greet",
    /* ERROR */ [](Person* p, Value v) {
      p->greet(value_to_std_string(v));
  });
}

I'm fairly confident that I'm not misusing lib1.h's API (and implementing such methods by deriving the classes of lib2.h is unfortunately not an option). Therefore I feel that casting to method pointers is the only solution.

Is there any way of legally doing this? I would like to avoid undefined behavior if possible.


Solution

  • No you cannot do such cast. Even if you could get a void(Person::*)(Value), you could not use that because Person has no such method. If lib1 expects a type C with a member function void(C::)(Value) then there is no way around providing such type C with the corresponding method. You can write a wrapper for Person:

    struct Wrapper {    
        Person p;
        void greet(Value v) {
            p.greet(value_to_std_string(v));
        }
    };
    
    
    Property declarePersonGreet() {
      return ObjectWrap<Wrapper>::declare("greet",&Wrapper::greet);
    }
    

    If you have many classes similar to Person you could make the wrapper a template:

    template <typename T>
    struct Wrapper {    
        T p;
        void greet(Value v) {
            p.greet(value_to_std_string(v));
        }
    };
    

    ...possibly also parameterized on the member function to be called on the wrapped object and the function to be used for converting the parameter.