Frage

Ist es möglich, eine Reihe von Templat Funktionszeigern, ohne den Aufwand, dies zu tun manuell zu etablieren? Hier ist ein Beispiel zu veranschaulichen, was zum Teufel ich rede.

Lassen Sie uns sagen, ich habe eine häufig genannte Funktion „Schreiben“ von denen ich zwei Implementierungen (write0 und Write1), Ich mag würde der Lage sein, zwischen dynamisch zu wechseln. Diese Schreibfunktionen sind auf dem Argumenttyp Templat. Eine Möglichkeit, dies zu tun ist, hat nur eine Templat-Front-End-Funktion write (), die intern verwendet eine if-Anweisung.

Dies erweist sich als schnell genug sein, um für meine Bedürfnisse, aber jetzt war ich frage mich verlassen, wenn ich die gleichen mit Funktionszeiger (nur zum Spaß) tun können. Das Problem bei diesem Ansatz ist, dass der Funktionszeigers Einrichtung ist ein Streit. Gibt es andere Möglichkeiten, um im wesentlichen des Ideal der write () zu erreichen, aber ohne den bedingten (direkten statischen Versand)?

(Andere „Regeln“. Ich kann die Msg Klassen ändern Schreib haben () Methoden, und ich kann nicht die Verwendung Standortcode ändern Msgs mit Adaptern für Msgs zu ersetzen)

FWIW, ich fand diesem Artikel im Grunde sagt das gleiche, was ich bin hier zu sagen.

#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;
}
War es hilfreich?

Lösung

Wenn Sie Protokollierungsfunktionen wechseln mögen hin und her, während das Programm läuft, ich glaube, Sie müssen manuell für jeden Typen die Funktionszeiger setzen.

Wenn es genug ist, um nur die Logging-Funktion beim Start zu wählen, kann es ohne auch nur in einer vollständig generische Art und Weise durchgeführt werden wissen, für welche Typen die Funktion später aufgerufen wird:

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

Für jeden Typ T der erste Aufruf von write<T>() die Schreibfunktion entscheidet, verwendet werden soll. Später Anrufe an diese Funktion, um die Funktionszeiger direkt dann verwenden.

Andere Tipps

Sie können auch Don Clugston die Verwendung FastDelegates Header. Erzeugt keinen Laufzeitaufwand überhaupt und wirklich objektorientiert Delegierten. Während die Syntax für die Verwendung sie nicht perfekt ist, ist es ein bisschen einfacher als mit rohen Funktionszeigern Hantieren.

Warum Sie nicht über ein Array von Funktionszeigern verwenden?

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

Es gibt zwei Achsen Variations schriftlich: die write0 / Write1 Wahl und die MSGA / B / C .... Wahl.

Konzeptionell bedeutet das müssen Sie NxM Implementierungen einer write Funktion. Natürlich, wenn eine Schreib Implementierung hinzugefügt wird, oder ein Nachrichtentyp hinzugefügt wird, führt dies zu resp. M oder N zusätzliche Funktionen hinzugefügt werden.

Für beide Achsen können Sie wählen, ob sie statisch oder dynamisch Polymorphismus zu implementieren. Der statische Polymorphismus kann mit Hilfe von Vorlagen oder mit der Funktion überschreibt erfolgen.

Es könnte durch die Schaffung einer N-Element Klassenhierarchie mit M Schreibfunktionen in jeder Klasse erfolgen. Aber es würde bald ein Wartungs Alptraum werden. Es sei denn, der Inhalt der Nachricht Laufzeit ist auch polymorph. Aber die Frage ist, über statischen Polymorphismus für die Nachrichten.

Da Runtime Polymorphismus ist wegen zu aufwendig ausgeschlossen (und Sie können keine Template-Funktion virtuelle haben, die den Detaillierungsgrad von Überschreibungen verringern würde), brauchen wir eine kleine Art-Dispatching-Routine zu implementieren, Laufzeitinformationen in der Kompilierung Umwandlung -Zeit Informationen.

Genauer gesagt:. Templatize die Hauptaktion (im Beispiel genannt Tmain) mit dem Schriftsteller-to-use, und rufen Sie mit der rechten Template-Argument von der ‚realen‘ main

Dies läßt die Verwendung einer ‚globalen‘ Wahl variabel, doch ist objektorientiert und prägnant.

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

    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top