Вызов функции-члена с голым указателем на функцию

Как лучше всего вызвать функцию-член, если у вас есть объект и голый указатель функции, указывающий на член? По сути, я хочу вызвать указатель функции с соглашением о вызовах thiscall.

Предыстория: я динамически просматриваю символы в разделяемой библиотеке, получая указатель фабричной функции и указатель на определенную функцию-член, которую я хочу вызвать. Сама функция-член не является виртуальной. У меня нет контроля над общей библиотекой, у меня есть только бинарный файл.

Пример:

typedef void * (*GenericFptr)();
GenericFptr lookup(const char *);

class CFoo;

GenericFptr factoryfn(lookup("CFoo factory function"));
CFoo *foo = reinterpret_cast<CFoo *>(factoryfn());

GenericFptr memberfn(lookup("CFoo member function"));

// now invoke memberfn on foo

В настоящее время я использую union для преобразования указателя функции в указатель на функцию-член. Это уродливо и создает зависимости от деталей реализации компилятора:

class CFoo {
  public: 
  void *dummy() { return 0; }
};
typedef void * (CFoo::*FooMemberPtr)();

union {
  struct {
    // compiler-specific layout for pointer-to-member
    void *x, *y;
    GenericFptr ptr;
  } fnptr;
  FooMemberPtr memberfn;
} f;

f.memberfn = &CFoo::dummy; // init pointer-to-member
f.fnptr.ptr = memberfn;    // rewrite pointer

void *result = (foo->*f.memberfn)();

person laalto    schedule 30.06.2010    source источник
comment
как символы для функции-члена оказались в общем объекте? когда я пытаюсь это сделать, невиртуальные символы функций-членов не публикуются.   -  person Dima Tisnek    schedule 21.03.2014


Ответы (4)


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

Я не думаю, что есть какой-либо портативный способ сделать то, что вы пытаетесь сделать, хотя, если объединение работает, вам, вероятно, это сойдет с рук. Опять же, вам нужно знать представление и соглашение о вызовах для этих методов для каждого компилятора, который вы хотите использовать для построения тела.

Если вы знаете имя функции-члена, почему бы вам просто не сделать, например, foo->dummy()? В противном случае либо функция поиска должна предоставить полный указатель функции-члена, либо библиотека должна предоставить интерфейс оболочки C с обычными функциями, которым может быть передан этот указатель.

person Mark B    schedule 30.06.2010
comment
Спасибо. Прямой вызов функции невозможен, поскольку я не могу ссылаться на библиотеку во время компиляции. Кроме того, функция поиска предоставляется базовой операционной системой и не может быть легко заменена. - person laalto; 30.06.2010
comment
Думаю, ты ошибаешься, Марк. У Лаллато 2 указателя. Учитывая указатель на экземпляр и адрес функции-члена, вполне возможно вызвать функцию-член для экземпляра. Это явно нетривиально для виртуальной функции и возможных даже не виртуальных функций, определенных в классе, который имеет виртуальные функции. Невиртуальная функция в простом классе должна быть простой, однако переносимое приложение по-прежнему требует знания или типа в некоторых компиляторах. Я приготовлю пример в GCC, который проще всего. - person Dima Tisnek; 21.03.2014

Указатель на функцию-член не может храниться в указателе на функцию, потому что ему требуется дополнительная информация (например, в случае множественного наследования перед вызовом может потребоваться применить к нему смещение). Так что без знания деталей реализации не обойтись.

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

person AProgrammer    schedule 30.06.2010
comment
Спасибо. Я знаю достаточно деталей реализации, чтобы трюк с объединением работал, но мне бы не хотелось этого делать. Я не могу коснуться самой библиотеки, поскольку она уже развернута на сотнях миллионов экземпляров. - person laalto; 30.06.2010
comment
Будет ли приемлемо добавление второй библиотеки, которая предоставляет оболочку? Проблема в том виде, в каком вы ее формулируете, не имеет стандартного решения, поэтому вам придется изменить свой подход. Не зная всего контекста, довольно сложно что-то предлагать. - person AProgrammer; 30.06.2010

Следующие две ссылки дают понимание и, возможно, решение. Обратите внимание, что вызов указателя функции-члена с аргументом this обычно не работает, поскольку вы должны учитывать виртуальные методы, множественное и виртуальное наследование.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

http://www.codeproject.com/KB/cpp/ImpossibleFastCppDelegate.aspx

person Alexandre C.    schedule 30.06.2010

Согласно приведенному ниже ответу, это выполнимо в GCC, но не переносимо:

https://stackoverflow.com/a/5067992/705086

person Dima Tisnek    schedule 21.03.2014