Вызов указателя функции C ++ на конкретный экземпляр объекта

StackOverflow https://stackoverflow.com/questions/151418

  •  02-07-2019
  •  | 
  •  

Вопрос

У меня есть указатель на функцию, определенный с помощью:

typedef void (*EventFunction)(int nEvent);

Есть ли способ обработать эту функцию с конкретным экземпляром объекта C ++?

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

Редактировать: Орион указал на опции, которые предлагает Boost, такие как:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

К сожалению, Boost - это не вариант для меня.Есть ли какая-то функция "каррирования", которая может быть написана на C ++, которая будет выполнять такого рода перенос указателя на функцию-член в обычный указатель на функцию?

Это было полезно?

Решение

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

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

Другие советы

Вы можете использовать указатели на функции для индексации в vtable данного экземпляра объекта.Это называется указатель на функцию-член.Ваш синтаксис необходимо было бы изменить, чтобы использовать операторы ".*" и "&::":

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

а потом:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};

Убегайте от необработанных указателей на функции C ++ и используйте std::function вместо этого.

Вы можете использовать boost::function если вы используете старый компилятор, такой как Visual Studio 2008, который не поддерживает C ++ 11.
boost:function и std::function это одно и то же - они добавили довольно много boost-материала в std-библиотеку для C ++ 11.

Примечание:возможно, вы захотите ознакомиться с документацией по функции boost, а не с документацией Microsoft, поскольку ее легче понять

Вы можете найти Часто задаваемые вопросы по C ++ от Маршалла Клайна полезно для того, чего вы пытаетесь достичь.

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

Если вы взаимодействуете с библиотекой C, то вы не можете использовать функцию-член класса, не используя что-то вроде boost::bind.Большинство библиотек C, которые принимают функцию обратного вызова, обычно также позволяют вам передавать дополнительный аргумент по вашему выбору (обычно типа void*), который вы можете использовать для начальной загрузки вашего класса, таким образом:


class C
{
public:
  int Method1(void) { return 3; }
  int Method2(void) { return x; }

  int x;
};

// This structure will hold a thunk to
struct CCallback
{
  C *obj;  // Instance to callback on
  int (C::*callback)(void);  // Class callback method, taking no arguments and returning int
};

int CBootstrapper(CCallback *pThunk)
{
  // Call the thunk
  return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}

void DoIt(C *obj, int (C::*callback)(void))
{
  // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
  struct CCallback thunk = {obj, callback};
  foobar(&CBootstrapper, &thunk);
}

int main(void)
{
  C c;
  DoIt(&c, &C::Method1);  // Essentially calls foobar() with a callback of C::Method1 on c
  DoIt(&c, &C::Method2);  // Ditto for C::Method2
}

К сожалению, тип EventFunction не может указывать на функцию из B, потому что это неправильный тип.Вы могли бы придать ему правильный тип, но, вероятно, это не совсем то решение, которое вам нужно:

typedef void (*B::EventFunction)(int nEvent);

...и тогда все заработает, как только вы вызовете обратный вызов с объектом B.Но вы, вероятно, хотите иметь возможность вызывать функции за пределами B, в других классах, которые делают другие вещи.В этом своего рода смысл обратного вызова.Но теперь этот тип определенно указывает на что-то в B.Более привлекательными решениями являются:

  • Сделайте B базовым классом, затем переопределите виртуальную функцию для каждого другого класса, который может быть вызван.Затем A сохраняет указатель на B вместо указателя на функцию.Намного чище.
  • Если вы не хотите привязывать функцию к определенному типу класса, даже к базовому классу (и я бы не стал вас винить), тогда я предлагаю вам сделать функцию, которая вызывается, статической функцией:"static void EventFrom A(int nEvent);".Тогда вы можете вызвать его напрямую, без объекта B.Но вы, вероятно, хотите, чтобы он вызывал конкретный экземпляр B (если только B не является синглтоном).
  • Итак, если вы хотите иметь возможность вызывать определенный экземпляр B, но при этом иметь возможность вызывать и не-B, то вам нужно передать что-то еще вашей функции обратного вызова, чтобы функция обратного вызова могла вызвать нужный объект.Сделайте вашу функцию статической, как указано выше, и добавьте параметр void *, который вы сделаете указателем на B.

На практике вы видите два решения этой проблемы:специальные системы, в которых вы передаете void * и событие, а также иерархии с виртуальными функциями в базовом классе, например оконные системы

Вы упомянули, что boost для вас не вариант, но доступен ли вам TR1?

TR1 предлагает объекты function , bind и mem_fn на основе библиотеки boost, и, возможно, она уже есть в комплекте с вашим компилятором.Это еще не стандартно, но по крайней мере в двух компиляторах, которыми я недавно пользовался, это было.

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx

Несколько неясно, чего вы пытаетесь здесь добиться.что ясно, так это то, что указатели на функции - это не выход.

возможно, то, что вы ищете, - это указатель на метод.

У меня есть набор классов именно для этой вещи, которые я использую в своем фреймворке c ++.

http://code.google.com/p/kgui/source/browse/trunk/kgui.h

Как я с этим справляюсь, каждая функция класса, которая может быть использована в качестве обратного вызова, нуждается в статической функции, которая привязывает к ней тип объекта.У меня есть набор макросов, которые делают это автоматически.Он создает статическую функцию с тем же именем, за исключением префикса "CB_" и дополнительного первого параметра, который является указателем объекта класса.

Проверьте типы классов kGUICallBack и различные их версии шаблонов для обработки различных комбинаций параметров.

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top