mit Templateargument Methoden "entfernen"?



  • Ich habe folgendes Szenario / Idee

    template </*...*/>
    class X {
        void read(/*...*/) { /*...*/ }
        void write(/*...*/) { /*...*/ }
    }
    

    Ich würde gerne, mittels eines Templatearguments, nur schreiben, lesen oder beides.
    Kann ich dem Compiler dazu bringen mir dann auch einen Fehler zu werfen, bei falscher Benutzung?
    sozusagen ein ios_base::openmode zur Kompilierzeit.
    Geht das überhaupt? Wenn nicht, was gäb es für gute Alternativen?



  • spezialisieren?

    enum class openmode
    {
        read,
        write
    };
    
    template <openmode>
    class X 
    {
    };
    
    template<>
    class X<openmode::read>
    {
        void read(/*...*/) { /*...*/ }
    };
    
    template<>
    class X<openmode::write>
    {
        void write(/*...*/) { /*...*/ }
    };
    

    (Ungetestet, C++11)



  • #include <type_traits>
    
    struct Reader
    {
        void read();
    };
    
    struct Writer
    {
        void write();
    };
    
    struct Empty {};
    
    template <bool R, bool W>
    struct X
        : public typename std::conditional<R, Reader, Empty>::type
        , public typename std::conditional<W, Writer, Empty>::type
    {
    };
    

    Statt bool könntest du natürlich auch Flags nehmen.

    [Edit] ::type vergessen



  • @Nexus: Du hast ::type vergessen "std::conditional<R, Reader, Empty>::type"



  • oder beides.

    Hab ich übersehen. :xmas1:

    Passe meine Lösung an.

    Edit: Nimm gleich Nexus', meine läuft auch nur aufs ableiten hinaus..



  • Oder Alternativ:

    struct Reader 
    { 
        void read(); 
    }; 
    
    struct Writer 
    { 
        void write(); 
    };   
    
    template <tyename... StreamPolicies> 
    struct X : public StreamPolicies... 
    { 
    };
    


  • Ganz anderer Ansatz:

    template <OpenMode OM>
    struct X
    { 
      typename std::enable_if<OM & Read>::type read();
      typename std::enable_if<OM & Write>::type write();
    
      bool is_open() const;
    };
    

    Hat den Vorteil, dass manche Methoden von beiden benutzt werden können (is_open).



  • enabler schrieb:

    Ganz anderer Ansatz:

    template <OpenMode OM>
    struct X
    { 
      typename std::enable_if<OM & Read>::type read();
      typename std::enable_if<OM & Write>::type write();
    
      bool is_open() const;
    };
    

    Hat den Vorteil, dass manche Methoden von beiden benutzt werden können (is_open).

    Das wird nicht funktionieren.



  • Sone schrieb:

    Das wird nicht funktionieren.

    Stimmt, da braucht es etwas mehr Aufwand:

    template <bool R, bool W>
    struct X {
      template <bool Enabled = R,
                typename = typename std::enable_if<Enabled>::type>
      void read() {}
      template <bool Enabled = W,
                typename = typename std::enable_if<Enabled>::type>
      void write() {}
      bool is_open() const {};
    };
    

    Jetzt geht es aber.



  • Mir ist noch eine Möglichkeit eingefallen, die ohne Vererbung funktionieren könnte (aber nur wenn read und write keine Prozeduren, sondern Funktionen sind, was bei mir aber eh der Fall ist):

    EDIT: Der Fehler ist aber nicht eindeutig / unverständlich

    struct wrong {
        virtual void _f() = 0;
    };
    
    template <bool R, bool W>
    struct X 
    {
        typename std::conditional<R, int, wrong>::type read() {
            /* ... */
            return 0;
        }
    
        typename std::conditional<W, int, wrong>::type write() {
            /* ... */
            return 0;
        }
    };
    

    EDIT 2: Die Lösung von "enabler" ist sehr viel schöner.



  • enabler schrieb:

    Sone schrieb:

    Das wird nicht funktionieren.

    Stimmt, da braucht es etwas mehr Aufwand:

    template <bool R, bool W>
    struct X {
      template <bool Enabled = R,
                typename = typename std::enable_if<Enabled>::type>
      void read() {}
      template <bool Enabled = W,
                typename = typename std::enable_if<Enabled>::type>
      void write() {}
      bool is_open() const {};
    };
    

    Jetzt geht es aber.

    Ja, aber das ganze mit SFINAE zu lösen ist irgendwie voll Overkill, findest du nicht?
    Ich finde Nexus' Lösung ist die beste.



  • Zumindest müsste ich mit der Version von "enabler" nicht alles umbauen...


  • Mod

    Was spricht gegen

    template <bool R, bool W>
    struct X {
      void read()
      {
          static_assert( R, "read not enable for this type!" );
          // ...
      }
      void write()
      {
          static_assert( W, "write not enable for this type!" );
          // ...
      }
      bool is_open() const {};
    };
    

    ?



  • Ok, campers ist die Beste.

    Ein einfaches static_assert dürfte es schon tun 😃

    Bzw. Bei C++98/03 einfach [c]BOOST_REQUIRE_MESSAGE[/c] BOOST_STATIC_ASSERT .



  • Sone schrieb:

    Ok, campers ist die Beste.

    Ein einfaches static_assert dürfte es schon tun 😃

    Bzw. Bei C++98/03 einfach BOOST_REQUIRE_MESSAGE .

    Was soll BOOST_REQUIRE_MESSAGE in dem Zusammenhang? Das ist aus Boost.Test und macht einen RUNTIME Check. Also vollkommen daneben.

    Wenn static_assert nicht verfügbar ist nimmt man einfach BOOST_STATIC_ASSERT .



  • camper schrieb:

    Was spricht gegen [static_assert]?

    Das habe ich mir auch überlegt, war aber verunsichert, was den Auswertungszeitpunkt angeht.

    Ist es also garantiert, dass static_assert erst angeschaut wird, wenn die entsprechende Methode instanziiert wird?


  • Mod

    Nexus schrieb:

    Ist es also garantiert, dass static_assert erst angeschaut wird, wenn die entsprechende Methode instanziiert wird?

    Nein. In diesem Falle ist aber die Bedingung vom Templateparameter abhängig und kann folglich erst bei der Instantiierung ausgewertet werden.



  • hustbaer schrieb:

    Sone schrieb:

    Ok, campers ist die Beste.

    Ein einfaches static_assert dürfte es schon tun 😃

    Bzw. Bei C++98/03 einfach BOOST_REQUIRE_MESSAGE .

    Was soll BOOST_REQUIRE_MESSAGE in dem Zusammenhang? Das ist aus Boost.Test und macht einen RUNTIME Check. Also vollkommen daneben.

    Ups.
    Hab es nur einige Sekunden vorher benutzt, deswegen die Verwechselung :xmas2:

    Muss natürlich ~_STATIC_ASSERT sein.



  • camper schrieb:

    Nexus schrieb:

    Ist es also garantiert, dass static_assert erst angeschaut wird, wenn die entsprechende Methode instanziiert wird?

    Nein. In diesem Falle ist aber die Bedingung vom Templateparameter abhängig und kann folglich erst bei der Instantiierung ausgewertet werden.

    aber die sind doch in dem Moment vorhanden wo die Klasse instanziiert wird. Und zum Beispiel der gcc versucht dann jede Funktion zu instanziieren, selbst wenn sie nicht aufgerufen wird.



  • otze schrieb:

    camper schrieb:

    Nexus schrieb:

    Ist es also garantiert, dass static_assert erst angeschaut wird, wenn die entsprechende Methode instanziiert wird?

    Nein. In diesem Falle ist aber die Bedingung vom Templateparameter abhängig und kann folglich erst bei der Instantiierung ausgewertet werden.

    aber die sind doch in dem Moment vorhanden wo die Klasse instanziiert wird. Und zum Beispiel der gcc versucht dann jede Funktion zu instanziieren, selbst wenn sie nicht aufgerufen wird.

    Darf er das laut Standard überhaupt?


  • Mod

    otze schrieb:

    aber die sind doch in dem Moment vorhanden wo die Klasse instanziiert wird. Und zum Beispiel der gcc versucht dann jede Funktion zu instanziieren, selbst wenn sie nicht aufgerufen wird.

    Sollte er eigentlich nicht, sofern es nicht eine explizite Instantiierung der Klasse oder aber die Funktion selbst virtuell ist.


Anmelden zum Antworten