Enums mit bool belegen als Compile-Time-Schalter



  • Hallo zusammen,

    ich spiele gerade mit Compile-Time-Entscheidungen.

    Für einen Nachrichtenparser habe ich zwei Dummy-Klassen, mit denen man den Aufruf mal für einen 'Request' und mal für eine 'Answer' zurechtschneidern kann. Der Punkt ist, dass der Großteil des Codes (hier symbolisch die Funktion 'parse') identisch ist und sich nur in Kleinigkeiten unterscheidet.

    Daher möchte ich versuchen, das nicht über unterschiedliche Funktionen bzw. Polymorphie oder Flag-Argumente (Laufzeitentscheidung) zu erreichen, sondern darüber, dass die Codeteile, die nicht benötigt werden, gar nicht erst compiliert werden (Compile-Time).

    Somit soll parse() per Template in zwei Varianten erstellt werden können.

    An einer Stelle verwende ich Request und Answer für ein tag dispatching mit überladenen Funktionen, aber im Beispiel unten ist der Codeblock, der nur bei 'Request' ausgeführt werden soll, recht kurz. Tag dispatch würde hier für 'Answer' eine leere Funktion erfordern, zudem müssen einige Funktionsargumente mitgegeben werden, was die Sache komplizierter macht als notwendig.

    Die if-Anweisung finde ich viel eleganter, und als Entscheidungstrigger brauche ich bool-Werte. Da man nicht direkt nach Typen vergleichen kann ( if(Role==Request) ), missbrauche ich die enums IS_REQUEST dafür:

    struct Request
    {
        enum { IS_REQUEST = true };
    };
    
    struct Answer
    {
        enum { IS_REQUEST = false };
    };
    
    template <typename Role>
    void parse( const string& msg )
    {
        cout << "Do something with message: " << msg << endl;
    
        if( Role::IS_REQUEST )  // Soll nur bei Requests compiliert werden
        {
            cout << "I am acting on a request!" << endl;
        }
    
        cout << "Do something else" << endl;
    }
    
    int main()
    {
        string req = "Hello";
        parse<Request>( req );
    
        string ans = "World";
        parse<Answer>( ans );
    

    Ausgabe:

    `Do something with message: Hello

    I am acting on a request!

    Do something else

    Do something with message: World

    Do something else`

    Achtung, es ist C++98!

    Spricht grundsätzlich etwas dagegen, enums mit true oder false zu belegen? In C++11 kann man ja den unterlegten Typ definieren, sollte da auch mit bool gehen...

    Weiß jemand noch eine schicke andere Lösung (gerne auch C++11)?


  • Mod

    true und false sind letztlich auch nur Zahlen, wie alles andere im Computer auch. Genauer gesagt hat true den Wert 1 und false hat den Wert 0. Es spricht also nichts dagegen, diese beiden Schlüsselworte an Stellen zu verwenden, an denen integrale Werte erwartet werden. ~(Ergänzung, um die Superklugscheißer ruhig zu stellen: true und false haben laut Standard keinen festgelegten Wert. Es ist bloß definiert, dass false zu 0 konvertiert wird und true zu 1 und umgekehrt 0 zu false und nicht-0 zu true. Was effektiv auf das hinaus läuft, was ich hier gesagt habe)~

    Da man nicht direkt nach Typen vergleichen kann ( if(Role==Request) )

    Kann man schon. Man stopft eben beide in ein Template, das eine Spezialisierung für zwei gleiche Typen hat. Was in diesem Fall aber wohl keinen großen Vorteil bringt; so wie es jetzt ist, ist es schon ok.

    Das was du hier machst ist eine Form von policy-based design. Du scheinst schon die wesentlichen Teile aufgeschnappt zu haben, aber wenn du mehr darüber wissen möchtest, hast du nun ein Stichwort.



  • SeppJ schrieb:

    ... ist eine Form von policy-based design.

    Stimmt, nur dass die Policies eben keine Funktionalität, sondern "nur" ein Entscheidungskriterium enthalten.

    SeppJ schrieb:

    Man stopft eben beide in ein Template, das eine Spezialisierung für zwei gleiche Typen hat. Was in diesem Fall aber wohl keinen großen Vorteil bringt;

    Danke für den Tip!

    Das bringt schon einen Eleganz-Vorteil: Was mich an meiner Lösung gestört hatte war, dass
    a) ich die enums mit den Bool-Werten überhaupt brauche und ich nicht direkt die Klasse als Entscheidungskriterium nutzen konnte, und
    b) in der Klasse 'Answer' ein enum 'IS_REQUEST = false' heißt. So als würde man in Klasse 'Haus' sagen: IS_BAUM = false. Stört mich irgendwie.

    Hier also mit is_same:

    // C++98-Implementierung von is_same. Mit C++11 std::is_same benutzen
    template<class T, class U>
    struct is_same
    {
        enum { value = false };
    };
    
    template<class T>
    struct is_same<T, T>
    {
        enum { value = true };
    };
    
    //----------------
    
    struct Request
    {};
    
    struct Answer
    {};
    
    template <typename Role>
    void parse( const string& msg )
    {
        cout << "Do something with message: " << msg << endl;
    
        if( is_same<Role, Request>::value )  // Soll nur bei Requests compiliert werden
        {
            cout << "I am acting on a request!" << endl;
        }
    
        cout << "Do something else" << endl;
    }
    


  • Mir ist noch was anderes eingefallen, eigentlich noch simpler. Wußte gar nicht, dass man Templates auch mit Enum-Argumenten machen kann (neben int und bool).
    Tag dispatching (an anderer Stelle) ginge dann über int2Type.

    enum Role_e
    {
        REQUEST,
        ANSWER
    };
    
    template <Role_e Role>
    void parse( const string& msg )
    {
        cout << "Do something with message: " << msg << endl;
    
        if( Role == REQUEST )  // Soll nur bei Requests compiliert werden
        {
            cout << "I am acting on a request!" << endl;
        }
    
        cout << "Do something else" << endl;
    }
    
    int main()
    {
        string req = "Hello";
        parse<REQUEST>( req );
    
        string ans = "World";
        parse<ANSWER>( ans );
    }
    

Anmelden zum Antworten