Как легально привести функцию указатель на метод указатель? - PullRequest
1 голос
/ 29 апреля 2020

Я ищу противоположность std::mem_fn: превращение указателя функции в указатель члена (например, void(*)(C*) до void(C::*)() для данного класса C).

Фон

Я использую стороннюю библиотеку (скажем, lib1.h ) для создания привязок для другой сторонней библиотеки ( lib2.h ).

Некоторые функции в lib1.h получают указатели на методы, которые принимают аргумент Value (также определенный в lib1 .h ). С другой стороны, классы lib2.h не содержат таких методов, поэтому мне приходится делать обертывания вручную с помощью лямбда-функции:

/* 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));
  });
}

Я вполне уверен, что я ' я не злоупотребляю API lib1.h (и реализация таких методов путем получения классов lib2.h , к сожалению, не вариант). Поэтому я чувствую, что приведение к указателям на методы - единственное решение.

Есть ли какой-либо способ сделать это на законных основаниях? Я хотел бы избежать неопределенного поведения, если это возможно.

1 Ответ

2 голосов
/ 29 апреля 2020

Нет, вы не можете делать такие актеры. Даже если бы вы могли получить void(Person::*)(Value), вы не могли бы его использовать, потому что Person не имеет такого метода. Если lib1 ожидает тип C с функцией-членом void(C::)(Value), то нет способа предоставить такой тип C соответствующим методом. Вы можете написать обертку для 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);
}

Если у вас много классов, похожих на Person, вы можете сделать обертку шаблоном:

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

... возможно также параметризован для функции-члена, вызываемой для обернутого объекта, и функции, которая будет использоваться для преобразования параметра.

...