Domanda

E 'possibile stabilire una serie di puntatori a funzione su modelli, senza il fastidio di farlo manualmente? Ecco un esempio per illustrare cosa diavolo sto parlando.

Diciamo che ho una funzione chiamato di frequente "scrittura" di cui ho due implementazioni (write0 e write1) che mi piacerebbe essere in grado di passare tra dinamicamente. Queste funzioni di scrittura sono basati su modelli sul tipo di argomento. Un modo per farlo è quello di avere solo una funzione template front-end write (), che usa internamente un'istruzione if.

Questa risulta essere abbastanza veloce per le mie esigenze, ma ora è stato lasciato chiedendo se posso fare lo stesso usando puntatori a funzione (solo per divertimento). Il problema di questo approccio è che la creazione degli puntatori a funzione è una seccatura. Ci sono altri modi per raggiungere sostanzialmente l'ideale di write () ma senza la condizionale (invio statica diretta)?

(Altri "regole":. Non posso cambiare le classi Msg avere write () metodi, e non riesco a modificare il codice uso sito per sostituire Msgs con adattatori per Msgs)

FWIW, ho trovato questo articolo fondamentalmente dicendo la stessa cosa che sto dicendo qui.

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

// This isn't so bad, since it's just a conditional (which the processor will
// likely predict correctly most of the time).
bool use_write0;
template<typename T> void write(T msg) { if (use_write0) write0(msg); else write1(msg); }

struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

// This doesn't work: templates may not be virtual.
#if 0
struct Writer { template<typename T> virtual void write(T msg) = 0; };
struct Writer0 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
struct Writer1 { template<typename T> virtual void write(T msg) { cout << "write0: " << msg.name() << endl; } };
#endif

int main(int argc, char **argv) {
  use_write0 = argc == 1;

  // I can do this:
  write(MsgA());

  // Can I achieve the following without the verbosity (manual setup, named
  // template instantiations, etc.)?
  void (*pwriteA)(MsgA) = use_write0 ? (void(*)(MsgA)) write0<MsgA> : (void(*)(MsgA)) write1<MsgA>;
  void (*pwriteB)(MsgB) = use_write0 ? (void(*)(MsgB)) write0<MsgB> : (void(*)(MsgB)) write1<MsgB>;
  void (*pwriteC)(MsgC) = use_write0 ? (void(*)(MsgC)) write0<MsgC> : (void(*)(MsgC)) write1<MsgC>;
  void (*pwriteD)(MsgD) = use_write0 ? (void(*)(MsgD)) write0<MsgD> : (void(*)(MsgD)) write1<MsgD>;
  pwriteA(MsgA());
  pwriteB(MsgB());
  pwriteC(MsgC());
  pwriteD(MsgD());

  return 0;
}
È stato utile?

Soluzione

Se si desidera passare funzioni di registrazione avanti e indietro mentre il programma viene eseguito, io penso che si debba impostare manualmente il puntatore a funzione per ogni tipo.

Se è abbastanza per scegliere solo la funzione di registrazione in fase di avvio, può essere fatto in un modo completamente generico senza nemmeno sapere per quali tipi la funzione verrà chiamata in seguito:

// writer functions
template<typename T> void write0(T msg) { std::cout << 0; };
template<typename T> void write1(T msg) { std::cout << 1; };

// global flag
bool use_write0;

// function pointers for all types
template<typename T>
struct dispatch {
   typedef void (*write_t)(T);
   static write_t ptr;
};

// main write function
template<typename T>
inline void write(T msg) {
   (*dispatch<T>::ptr)(msg);
}

// the fun part
template<typename T>
void autoinit(T msg) {
   if (use_write0)
      dispatch<T>::ptr = &write0<T>;
   else
      dispatch<T>::ptr = &write1<T>;
   // call again for dispatch to correct function
   write(msg);
}

// initialization
template<typename T>
typename dispatch<T>::write_t dispatch<T>::ptr = &autoinit<T>;

// usage example
int main(int argc, char **argv) {
   use_write0 = (argc == 1);
   write("abc");
   return 0;
}

Per ogni tipo T la prima chiamata a write<T>() decide quale funzione di scrittura dovrebbe essere usato. le chiamate successive quindi utilizzare direttamente il puntatore a funzione a quella funzione.

Altri suggerimenti

Si potrebbe anche usare FastDelegates colpo di testa di Don Clugston. Genera alcun overhead runtime di sorta e veramente delegati orientato agli oggetti. Mentre la sintassi per il loro utilizzo non è perfetto, ma è un po 'più semplice rispetto a giocherellare con puntatori a funzione prime.

Perché non si utilizza un array di puntatori a funzione?

#include <iostream>
using namespace std;

template<typename T> void write0(T msg) { cout << "write0: " << msg.name() << endl; }
template<typename T> void write1(T msg) { cout << "write1: " << msg.name() << endl; }

template<typename T> struct WriteSelector
{
    static void(* const s_functions[])(T msg);
};
template<typename T> void(* const WriteSelector<T>::s_functions[])(T msg)=
{
    &write0<T>,
    &write1<T>
};

unsigned write_index=0;
template<typename T> void write(T msg)
{
    WriteSelector<T>::s_functions[write_index](msg);
}


struct MsgA { const char *name() { return "MsgA"; } };
struct MsgB { const char *name() { return "MsgB"; } };
struct MsgC { const char *name() { return "MsgC"; } };
struct MsgD { const char *name() { return "MsgD"; } };

void Test()
{
    write(MsgA());
    write(MsgB());
    write(MsgC());
    write(MsgD());
}

int main()
{
    Test();
    write_index=1;
    Test();
    return 0;
}

Ci sono due axises di variazione per iscritto: il / scelta write1 write0 e la scelta .... MsgA / B / C.

Concettualmente che significa che è necessario NxM implementazioni di una funzione write. Naturalmente, se si aggiunge un'implementazione di scrittura, o si aggiunge un tipo di messaggio, questo porta a resp. M o N funzioni extra da aggiungere.

Per entrambi axises si può scegliere se implementare utilizzando il polimorfismo statico o dinamico. Il polimorfismo statico può essere fatto utilizzando modelli o utilizzando le sostituzioni di funzione.

Potrebbe essere fatto creando una gerarchia di classi elemento N con funzioni M di scrittura in ogni classe. Ma sarebbe ben presto diventato un incubo di manutenzione. A meno che il contenuto del messaggio è runtime anche polimorfico. Ma la domanda è di circa il polimorfismo statico per i messaggi.

Dal momento che il polimorfismo runtime viene escluso a causa di troppo elaborato (e non si può avere una funzione template virtuale, che farebbe diminuire il livello di dettaglio delle sostituzioni), abbiamo bisogno di implementare una piccola routine tipo di dispacciamento, la conversione di informazioni di runtime nella compilazione informazioni -time.

In particolare:. Templatize causa principale (nell'esempio chiamato Tmain) con l'uso scrittore-to-, e chiamarlo con l'argomento modello fin dal main 'vero'

Questa omette l'uso di una variabile scelta 'globale', eppure è orientato agli oggetti e conciso.

    // twodimensionalpolymorph.cpp
    //

    #include <iostream>

    using namespace std;

    class Write0 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write0: " << msg.name() << endl; };
    };

    class Write1 {
        public: 
        template< typename tMsg > 
        void operator()( /*const*/ tMsg& msg ) { cout << "write1: "<< msg.name() << endl; };
    };

    struct MsgA { const char *name() { return "MsgA"; } };
    struct MsgB { const char *name() { return "MsgB"; } };
    struct MsgC { const char *name() { return "MsgC"; } };
    struct MsgD { const char *name() { return "MsgD"; } };

    // the Tmain does the real action
    //
    template< typename Writer >
    int Tmain( Writer& write, int argc, char** args ) {

        write( MsgA() );
        write( MsgB() );
        write( MsgB() );
        write( MsgD() );

        return 0;
    }

    // the main merely chooses the writer to use
    //
    int main( int argc, char** args ) {

        if( argc==1 )
            return Tmain( Write0(), argc, args);
        else
            return Tmain( Write1(), argc, args);

    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top