[C++09] boost::variant mit variadic templates nachbilden



  • hallo,

    aufgrund dieses beitrags habe ich mir ein paar gedanken zu einem typsicheren union-typen à la boost::variant gemacht.

    verwenden kann man es sogar schon, auf diese art und weise:

    int main ()
    {
       typedef variant<int, float, string> v_t;
       v_t v("test"), x(3);
    
       cout << v << x << endl;
    
       get<double>(x);//compile-time error
       get<float>(x); //runtime_error
    }
    
    //auch rekursive variants sind möglich (beispiel aus boost)
    struct add;
    struct sub;
    template <typename OpTag> struct binary_op;
    
    typedef variant<
          int
        , binary_op<add>
        , binary_op<sub>
        > expression;
    
    template <typename OpTag>
    struct binary_op
    {
        expression left;  // variant instantiated here...
        expression right;
    
        binary_op( const expression & lhs, const expression & rhs )
            : left(lhs), right(rhs)
        {
        }
    }; //boost::variant schafft das nicht.
    

    nur macht mir die implementation ein bisschen sorgen. kurz: ich suche hier nach alternativen design-ideen. (und vor allem eine idee, wie man hier mit policys arbeiten kann. aber dazu später)

    im grunde ist es nicht schwierig aufgebaut: variant ist über eine klassenhierarchie implementiert, die basisklasse variant_impl<> (ohne template-argumente) definiert eine rudimentäre schnittstelle zum umgang mit rohen speicher. jede stufe in der klassenhierarchie kümmert sich um einen eigenen typ und definiert eine statiche integer-konstante (eine type_id) um später beliebige typen finden zu können. (ähnlich typlisten)

    ich habe zwei probleme, die nach einer eleganteren lösung schreien (vielleicht auch mehr).

    das problem mit dem speicher: die unterste stufe der hierarchie bildet ein eigenes variadic template, nämlich "variant", das von variant_impl<Args...> erbt (noch öffentlich, aber das liegt nur an meiner tippfaulheit). diese (bzw. jede jeweils) unterste klasse stellt der basisklasse variant_impl den rohen speicher zur verfügung (einen zeiger auf ein char-array am stack mit der passenden größe). das ist möglich, da die variant_impl hierarchie durch virtuelle, geschützte (protected) vererbung implementiert ist. (eine der wenigen male, wo ich tatsächlich eine sinnvolle verwendung für virtual protected gefunden habe. womöglich lässt sich das aber auch anders implementieren.) - im prinzip ist es also möglich, die art der speicherverwaltung vom stack auf den heap zu verlegen, indem man eine andere klasse von variant_impl erben lässt (oder weiter von variant erbt). etwas umständlich, nur leider verbieten variadic templates es, hinten an noch policy template parameter anzugeben. policys nach vorne und auf template aliases warten? oder vielleicht ein ganz anderes design?

    dann gibt es ein zweites problem (ebenfalls gelöst), das konvertierungsfunktionen angeht. zunächst mal nur im konstruktor. diese probleme ergeben sich direkt aus dem faktum der vererbung.
    da jede variant_impl klasse sich nur um einen typen kümmert, stellt sie für diesen typen einen konstruktor bereit. die klasse muss aber auch für jeweils alle anderen typen einen konstruktor bereitstellen. diese aufgabe übernimmt - natürlich - ein template. dieser konstruktor sucht sich nun die hierarchie (typliste) nach einem exact match (std::is_same) ab, wenn er keinen findet, nimmt er den erstbesten std::is_convertible-typen. (das erlaubt so etwas:

    variant<string, int> v = "foo"; 
    //ansonsten explizit:
    variant<string, int> v = string("foo");
    

    )
    abgesehen davon, dass ich es template-metaprogrammierung immer noch hässlich finde, denn so etwas

    typedef typename select_type<
                                       typename variant_base::template get_matching_type<U>,
                                       typename variant_base::template get_perfect_type<U>
                                      >::type_t found_type_t;
    

    erklärt sich nicht gleich von selbst (g++ hat außerdem ein bisschen probleme mit typename in vbdg mit den variadic templates)

    ich würde es also kurz gesagt vorziehen, von der den vererbungsorgien abzukommen und das ganze flacher zu organisieren. nachdem ich mir aber die letzten stunden nur mehr gedanken über vererbung gemacht habe, scheint mein geist keine alternative mehr entwickeln zu wollen. hints?


Anmelden zum Antworten