Puntatori e classi di funzioni C++
-
09-06-2019 - |
Domanda
Diciamo che ho:
void Render(void(*Call)())
{
D3dDevice->BeginScene();
Call();
D3dDevice->EndScene();
D3dDevice->Present(0,0,0,0);
}
Questo va bene purché la funzione che voglio usare per il rendering sia una funzione o a static
funzione membro:
Render(MainMenuRender);
Render(MainMenu::Render);
Tuttavia, voglio davvero essere in grado di utilizzare anche un metodo di classe poiché nella maggior parte dei casi la funzione di rendering vorrà accedere alle variabili membro e preferirei non rendere globale l'istanza della classe, ad es.
Render(MainMenu->Render);
Tuttavia non ho davvero idea di come farlo e consento comunque le funzioni e static
funzioni membro da utilizzare.
Soluzione
Esistono molti modi per scuoiare questo gatto, inclusi i modelli.Il mio preferito è Funzione.Boost poiché ho trovato che sia il più flessibile a lungo termine.Leggi anche su Boost.bind per l'associazione alle funzioni membro e molti altri trucchi.
Sarebbe simile a questo:
#include <boost/bind.hpp>
#include <boost/function.hpp>
void Render(boost::function0<void> Call)
{
// as before...
}
Render(boost::bind(&MainMenu::Render, myMainMenuInstance));
Altri suggerimenti
Puoi creare una funzione wrapper void Wrap(T *t)
quello chiama e basta t->Call()
e avere Render
prendi una tale funzione insieme a un oggetto.Questo è:
void Wrap(T *t)
{
t->Call();
}
void Render(void (*f)(T *), T *t)
{
...
f(t);
...
}
E cosa? Domande frequenti su C++:Indicazioni ai membri dice?
L'ho fatto una volta definendo una funzione globale "Call" che accetta un puntatore alla tua istanza come membro
void CallRender(myclass *Instance)
{
Instance->Render();
}
Quindi il rendering diventa:
void Render(void (*Call)(myclass*), myclass* Instance)
{
...
Call(Instance);
...
}
E la tua chiamata per il rendering è:
Render(CallRender, &MainMenu);
So che è brutto, ma ha funzionato per me (stavo usando pthreads)
Non è possibile chiamare una funzione membro da un puntatore a meno che non si abbia anche un riferimento all'oggetto.Per esempio:
((object).*(ptrToMember))
Quindi non sarai in grado di raggiungere questo obiettivo senza modificare la firma del tuo metodo di rendering. Questo l'articolo spiega perché questa è generalmente una cattiva idea.
Un modo migliore potrebbe essere quello di definire un'interfaccia "Renderer" che le tue classi che hanno metodi di rendering possono implementare e che sia il tipo di parametro del tuo metodo Render principale.Potresti quindi scrivere un'implementazione "StaticCaller" per supportare la chiamata dei tuoi metodi statici per riferimento.
ad esempio (il mio C++ è davvero arrugginito, non ho compilato neanche questo).
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();
}
};
Tutto questo è piuttosto standard ma dovrebbe garantire una maggiore leggibilità.
È possibile dichiarare un puntatore a funzione a una funzione membro della classe T utilizzando:
typedef void (T::*FUNCTIONPOINTERTYPE)(args..)
FUNCTIONPOINTERTYPE function;
E invocarlo come:
T* t;
FUNCTIONPOINTERTYPE function;
(t->*function)(args..);
Estrapolarlo in un utile sistema di currying con argomenti variabili, tipi, valori restituiti, ecc. È monotono e fastidioso.Ho sentito parlare bene della suddetta libreria di boost, quindi consiglierei di esaminarla prima di fare qualcosa di drastico.