Pergunta

Eu tenho um ponteiro de função definida por:

typedef void (*EventFunction)(int nEvent);

Existe uma maneira de lidar com essa função com uma instância específica de um objeto 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 }
};

Editar: Orion apontou as opções que oferece impulso, tais como:

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

Infelizmente impulso não é uma opção para mim. Existe algum tipo de função "currying" que pode ser escrito em C ++ que vai fazer este tipo de embrulho de um ponteiro para uma função membro para um ponteiro de função normal?

Foi útil?

Solução

Eu recomendo excelente biblioteca FastDelegate de Don Clugston. Ele fornece todas as coisas que você esperaria de um delegado real e compila para baixo a algumas instruções ASM na maioria dos casos. O artigo que acompanha é uma boa leitura em ponteiros de função dos membros.

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

Outras dicas

Você pode usar ponteiros de função de índice para o vtable de uma determinada ocorrência de objeto. Isso é chamado de membro do ponteiro de função . Sua sintaxe seria necessário alterar a usar o e o '& ::' operadores "*.":

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

e, em seguida:

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

Executar longe de C ++ matéria ponteiros de função e uso std::function em seu lugar.

Você pode usar boost::function se você estiver usando um compilador antigo, como o Visual studio 2008, que não tem suporte para C ++ 11.
boost:function e std::function são a mesma coisa - eles puxaram um pouco de impulso material para a biblioteca std para C ++ 11

.

Nota: você pode querer ler a documentação função de aumento em vez da Microsoft como ele é mais fácil de entender

Você pode encontrar C ++ FAQ por Marshall Cline útil ao que você está tentando realizar.

Leia sobre ponteiros para membros . Para chamar um método na classe derivada, o método deve ser declarado na classe base como virtual e anulado na classe base e o ponteiro deve apontar para o método da classe base. Mais sobre ponteiros para membros virtuais .

Se você está interagindo com uma biblioteca C, então você não pode usar uma função membro da classe sem usar algo como boost::bind. A maioria das bibliotecas C que levam uma função de retorno normalmente também permitem que você passar um argumento extra de sua escolha (geralmente do tipo void*), que você pode usar para inicializar sua classe, assim:


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
}

Infelizmente, o tipo EventFunction não pode apontar para uma função de B, porque ele não é do tipo correto. Você poderia fazê-lo o tipo correto, mas que provavelmente não é realmente a solução que você deseja:

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

... e então tudo funciona uma vez que você chamar o callback com um obhect de B. Mas você provavelmente vai querer ser capaz de chamar funções fora do B, em outras classes que fazem outras coisas. Isso é uma espécie de ponto de um callback. Mas agora esse tipo aponta para algo definitivamente B. soluções mais atraentes são:

  • Faça B uma classe base, em seguida, substituir uma função virtual para cada outra classe que pode ser chamado. Um seguida, armazena um ponteiro para B em vez de um ponteiro de função. Muito mais limpo.
  • Se você não deseja vincular a função de um tipo de classe específica, mesmo uma classe base (e eu não culpo você), então eu sugiro que você faça a função que é chamada uma função estática: "static void EventFrom A(int nEvent);" . Depois, você pode chamá-lo diretamente, sem um objeto de B. Mas você provavelmente quer que ele chamar uma instância específica de B (a menos que B é um singleton).
  • Então, se você quer ser capaz de chamar uma instância específica de B, mas ser capaz de chamar não-B do, também, então você precisa passar alguma coisa para a sua função de retorno para que a função de retorno de chamada pode chamar o objeto certo . Fazer a sua função estática, como acima, e adicionar um parâmetro * vazio que você vai fazer um ponteiro para B.

Na prática, você vê duas soluções para este problema: sistemas ad hoc onde passam a * vazio e o evento, e hierarquias com funções virtuais em uma classe base, como sistemas de janelas

Você menciona que o impulso não é uma opção para você, mas você tem TR1 disponíveis para você?

Função ofertas TR1, bind, e mem_fn objetos com base na biblioteca de impulso, e você já pode tê-lo junto com seu compilador. Não é muito comum ainda, mas pelo menos dois compiladores que eu usei recentemente ter tido-lo.

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

É pouco claro o que você está tentando realizar aqui. o que está claro é que os ponteiros de função não é o caminho.

Talvez o que você está procurando ponteiro para método.

Eu tenho um conjunto de classes para essa coisa exata que eu uso no meu c ++ quadro.

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

Como lidar com isso é cada função classe que pode ser usado como um callback precisa de uma função estática que liga o tipo de objeto a ele. Eu tenho um conjunto de macros que fazem isso automaticamente. Faz uma função estática com o mesmo nome, exceto com um prefixo "CB_" e um primeiro parâmetro extra que é o ponteiro objeto de classe.

Checkout a classe tipos kGUICallBack e várias versões de modelos dos mesmos para o tratamento de diferentes parâmetros combinações.

#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);}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top