Ponteiros e classes de função C++
-
09-06-2019 - |
Pergunta
Digamos que eu tenha:
void Render(void(*Call)())
{
D3dDevice->BeginScene();
Call();
D3dDevice->EndScene();
D3dDevice->Present(0,0,0,0);
}
Isso é bom, desde que a função que desejo usar para renderizar seja uma função ou um static
função de membro:
Render(MainMenuRender);
Render(MainMenu::Render);
No entanto, eu realmente quero poder usar um método de classe também, já que na maioria dos casos a função de renderização desejará acessar variáveis de membro e prefiro não tornar a instância da classe global, por exemplo.
Render(MainMenu->Render);
No entanto, eu realmente não tenho ideia de como fazer isso e ainda permito funções e static
funções de membro a serem usadas.
Solução
Há muitas maneiras de esfolar esse gato, incluindo modelos.Meu favorito é Função Boost pois descobri que é o mais flexível no longo prazo.Leia também em Boost.bind para ligação a funções-membro, bem como muitos outros truques.
Ficaria assim:
#include <boost/bind.hpp>
#include <boost/function.hpp>
void Render(boost::function0<void> Call)
{
// as before...
}
Render(boost::bind(&MainMenu::Render, myMainMenuInstance));
Outras dicas
Você pode fazer uma função wrapper void Wrap(T *t)
isso só chama t->Call()
e tem Render
tome tal função junto com um objeto.Aquilo é:
void Wrap(T *t)
{
t->Call();
}
void Render(void (*f)(T *), T *t)
{
...
f(t);
...
}
O que sobre o que Perguntas frequentes sobre C++:Indicações para membros diz?
Fiz isso uma vez definindo uma função global "Call" que aceita um ponteiro para sua instância como membro
void CallRender(myclass *Instance)
{
Instance->Render();
}
Então a renderização se torna:
void Render(void (*Call)(myclass*), myclass* Instance)
{
...
Call(Instance);
...
}
E sua chamada para renderizar é:
Render(CallRender, &MainMenu);
Eu sei que é feio, mas funcionou para mim (eu estava usando pthreads)
Você não pode chamar uma função membro a partir de um ponteiro, a menos que também tenha uma referência ao objeto.Por exemplo:
((object).*(ptrToMember))
Portanto, você não conseguirá isso sem alterar a assinatura do seu método de renderização. Esse Este artigo explica por que isso geralmente é uma má ideia.
Uma maneira melhor seria definir uma interface "Renderer" que suas classes que possuem métodos de renderização possam implementar e que esse seja o tipo de parâmetro do seu método Render principal.Você poderia então escrever uma implementação "StaticCaller" para suportar a chamada de seus métodos estáticos por referência.
por exemplo (meu C++ está realmente enferrujado, também não compilei).
void Render(IRenderer *Renderer)
{
D3dDevice->BeginScene();
Renderer->Render();
D3dDevice->EndScene();
D3dDevice->Present(0,0,0,0);
}
// The "interface"
public class IRenderer
{
public:
virtual void Render();
};
public class StaticCaller: public IRenderer
{
void (*Call)();
public:
StaticCaller((*Call)())
{
this->Call = Call;
}
void Render()
{
Call();
}
};
Tudo isso é bastante clichê, mas deve proporcionar mais legibilidade.
Você pode declarar um ponteiro de função para uma função membro da classe T usando:
typedef void (T::*FUNCTIONPOINTERTYPE)(args..)
FUNCTIONPOINTERTYPE function;
E invoque-o como:
T* t;
FUNCTIONPOINTERTYPE function;
(t->*function)(args..);
Extrapolar isso para um sistema de curry útil com argumentos variáveis, tipos, valores de retorno, etc., é monótono e irritante.Ouvi coisas boas sobre a biblioteca de reforço mencionada acima, então recomendo dar uma olhada nisso antes de fazer qualquer coisa drástica.