Подтвердить что ты не робот

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

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

typedef void (*EventFunction)(int nEvent);

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

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 для меня не вариант. Есть ли какая-то функция "currying", которая может быть записана на С++, которая будет делать такую ​​оболочку указателя на функцию-член в обычный указатель функции?

4b9b3361

Ответ 1

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

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

Ответ 2

Удалите исходные указатели на функции С++ и используйте std::function.

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

Примечание. Возможно, вам захочется прочитать документацию функции повышения, а не версию microsoft, так как это проще понять.

Ответ 3

Вы можете использовать указатели функций для индексации в таблице 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 */ }
};

Ответ 5

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

Ответ 6

Если вы взаимодействуете с библиотекой 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
}

Ответ 7

К сожалению, тип 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.

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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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