using base_class::base_class;



  • ich brauche im grunde gesehen nur den datentyp string.
    ich baue viele URL parameter fuer webserver abfragen zusammen. hab da ca. 130 verschiedene parameter.
    nun koennte ich einfach ein std::pairstd::string,std::string nehmen und den namen und value des parameters reinschreiben und meinem stringstream zuweisen.
    ist aber fehleranfaellig.
    deshalb hab ich mir gedacht, das ich std::strings brauche die aber vom typ her trotzdem noch zu differenzieren sind und ich ihm auhc noch gleich den parameternamen als statischen cstring mitgeben kann

    hab es jetzt mal so geloest:

    template<class T>
    class string_class : public std::string
    {
       public:
          using std::string::string;
          static constexpr char *field_name = T::field_name;
    
          string_class(void) { }
          string_class(const string_class &s) : std::string(s) { }
          string_class(const std::string &str) : std::string(str) { }
    
          auto operator=(const string_class &s) -> string_class& { std::string::operator=(s); return *this; }
          auto operator=(const std::string &s) -> string_class& { std::string::operator=(s); return *this; }
    }; /* class string_class */
    
    struct account_traits { static constexpr char *field_name = "account"; };
    using account = string_class<account_traits>;
    
    template<class T>
    auto operator<<(std::ostream &out, const string_class<T> &s) -> std::ostream&
    {
       out << T::field_name << '=' << static_cast<const std::string&>(s);
    
       return out;
    }
    
    ...
    
    account my_account("test");
    
    std::cout << my_account;
    

    und er schreibt mir brav in den stream

    account=test
    

    klar haette ich dafuer auch eine funktion machen koennen:

    print_account(std::ostream &out, const std::string &acc) -> void
    { out << "account=" << acc; }
    

    aber das gefaellt mir weniger.

    ich mag data-driven APIs sowieso nicht und sie erleichtern das programieren auch nicht wirklich. es schaut immer alles so zusammengefrickelt aus, mit den strings dazwischen.



  • ziemlich sicher ist das nicht, was du willst - private vererbung wäre besser.

    account acc = "foo";
    
    cout << acc + "bar" << endl;
    //ausgabe: foobar, nicht account=foobar
    
    template<class T> 
    class string_class : private std::string 
    { 
       public: 
          using std::string::string; 
          static constexpr const char *field_name = T::field_name; //const über constexpr ist char*const, nicht const char*
    
          string_class() = default; 
    
        template <class Other>
          string_class(Other const&s) : std::string{s} { } 
    template <class Other>
          string_class(Other&& s) : std::string{std::move(s)} { } 
    
    template<class S> 
    friend auto operator<<(std::ostream &out, const string_class<S> &s) -> std::ostream& 
    { 
       out << T::field_name << '=' << static_cast<std::string const&>(s); 
    
       return out; 
    } 
    
    }; 
    
    struct account_traits { static constexpr const char *  field_name  = "account"; }; 
    using account = string_class<account_traits>;
    

    oder überhaupt etwas anderes? müsste man überlegen.



  • beachte die klassen in der STL sind nicht für Vererbung geeignet/gedacht (fehlender virtueller Destructor)



  • firefly schrieb:

    beachte die klassen in der STL sind nicht für Vererbung geeignet/gedacht (fehlender virtueller Destructor)

    ja ich weiß. aber ich erweitere die klasse ja nicht. insofern sollte das kein problem sein.

    @dove
    ich moechte mit string_class<T> genauso arbeiten koennen wie mit einem normalen std::string. deshalb erbe ich public.

    ist constexpr char *field_name = "name" nicht standard konform ?
    VC2015 frisst es mit Warnstufe 4 problemlos.

    dein beispiel mit der string-addition ist mir schon klar. aber die string_class verwende ich nur intern um mir das leben etwas einfacher zu machen.
    die string_classen sind in einem parameterpack zusammengefasst, das auch das streaming uebernimmt. da sehe ich keine probleme, da ich direkt nicht damit arbeite.
    einzeln schreibe ich die nie in einen stream.

    Meep Meep



  • nur ein aside, das c-literal via char* ist seit C++11 nicht mehr standardkonform, davor deprecated, was vielleicht erklärt, warum VC das immer noch erlaubt.

    wenn du slicing garantiert ausschließen kannst, spricht imo wohl nichts dagegen (außer prinzipielle gründe à la ist-ein vs ist-implementiert-als). dafür musst du halt selbst einen passenden copy-ctor schreiben.

    dir kann halt manchmal dann passieren, dass eine deiner string_class-instanzen zu einem string gemacht wird und das mit username=string dann nicht mehr funktioniert. das aneinanderreihen von mehreren solchen x=y mittels & stell ich mir auch nicht so flexibel vor.

    wie auch immer, je nach compiler-status kannst du dich hier auch mit (noch mehr) constexpr und non-type-template parametern spielen:

    template <char const* name>
    struct Parameter : public string { 
        using string::string;
        Parameter(string const& str) : string(str) {}
        Parameter(Parameter const&) = default;
        Parameter() = default;
    
    };
    
    template <char const* name>
    ostream& operator<< (ostream& out, Parameter<name> const& arg) {
        return out << name << "=" << static_cast<string const&>(arg); 
    }
    
    constexpr char const username_id[] = "username";
    using username = Parameter<username_id>;
    

    dann noch private vererbung und eventuell so etwas:

    template <char const* name>
    struct Parameter : private string { 
        using string::string;
        operator string& () { return name + *this; }
    //...
    


  • Warum keine stinknormale Aggregation? Warum überhaupt erben?



  • 5cript schrieb:

    Warum keine stinknormale Aggregation? Warum überhaupt erben?

    warum eine aggregation ? was soll mir das in dem fall bringen ?



  • dov schrieb:

    dann noch private vererbung und eventuell so etwas:

    template <char const* name>
    struct Parameter : private string {
        using string::string;
        operator string& () { return name + *this; }
    //...
    

    du meintest

    operator string () { return name + *this; }
    

    ?

    Meep Meep





  • 😃 an volkard

    und ja natürlich hätte ich operator string() schreiben sollen, man zeigt um 23:58 allerdings doch schon recht ausgeprägte ermüdungserscheinungen.

    @Meep Meep: deine ursprüngliche frage ist ja beantwortet, oder? eventuell kann man sich aber noch ein ganz alternatives design überlegen, wenn wir mehr über den einsatzbereich wüssten.



  • Meep Meep schrieb:

    5cript schrieb:

    Warum keine stinknormale Aggregation? Warum überhaupt erben?

    warum eine aggregation ? was soll mir das in dem fall bringen ?

    Ich wollte damit sagen, dass mir deine Ansatz nicht gefällt überhaupt irgendwie von string zu erben, damit sich deine klasse wie ein string einsetzen lässt.
    Dann lieber den namen aggregieren und spezielle memberfunktionen und Konvertierungsoperator selbst schreiben und ggf operator<< überladen.


Anmelden zum Antworten