2016-08-04 10 views
0

ich eine Baumhierarchie von Typen, die jeweils durch Zeichenfolge definiert, so etwas wie folgt aus:Typ Hierarchie basierend auf Zeichenfolge, mit der Kompilierung Typprüfung

com 
com.example 
com.example.shape 
com.example.shape.triangle 
com.example.shape.triangle.equilateral 
com.example.shape.triangle.isosceles 
com.example.shape.triangle.right 
com.example.shape.quadrilateral 
com.example.shape.quadrilateral.rectangle 
com.example.shape.quadrilateral.squere 

Typen einige Daten mit dynamischen Parametern definiert, dass in geändert werden Runtime, so dass es keine Möglichkeit gibt, eine Typhierarchie für die Kompilierzeit zu erstellen. Jede Entität ist also nur ein Typname (String) und eine Liste von Parametern, und Sie können immer einen neuen Typ im System registrieren. Trotzdem sind viele Typen vordefiniert und können beim Systemstart registriert werden. Um die gleiche Erfahrung mit Daten zu haben, die zur Laufzeit erstellt und vordefiniert wurden, verwende ich diese dynamische Darstellung für beide. Für vordefinierte Typen hätte ich gern einen Mechanismus, der den Typnamen zur Kompilierzeit validieren kann und ich möchte Zeichenfolgen nicht jedes Mal direkt in den Code einfügen, wenn er verwendet werden muss. Es kann durch die Definition von string const-Ausdrücken gelöst werden ist nicht sehr nett, etwas wie das:

string some_type = "com.example.type1"; 
... 
registerType(some_type, parameters_definition); 

So denke ich an einen besseren Weg.

Ein weiterer Ansatz ist, so etwas zu machen:

#include <iostream> 
#include <string> 

struct Base { 
    Base(std::string parent_name, std::string my_name) : name_(parent_name + "." + my_name) {} 
    std::string name_; 
}; 

std::ostream& operator<< (std::ostream& os, const Base& base) { 
    os << base.name_; 
    return os; 
} 

struct G : Base { 
    G(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
}; 

struct F : Base { 
    F(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
    G rectangle{name_, "rectangle"}; 
    G squere{name_, "squere"}; 
}; 

struct E : Base { 
    E(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
}; 

struct D : Base{ 
    D(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
    E equilateral{name_, "equilateral"}; 
    E isosceles{name_, "isosceles"}; 
    E right{name_, "right"}; 
}; 

struct C : Base { 
    C(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
    D triangle{name_, "triangle"}; 
    F quadrilateral{name_, "quadrilateral"}; 
}; 

struct B : Base{ 
    B(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {} 
    C shape{name_, "shape"}; 
}; 

struct A { 
    A(std::string my_name) : name_(my_name) {}; 
    std::string name_; 
    B example{name_, "example"}; 
}; 

std::ostream& operator<< (std::ostream& os, const A& a) { 
    os << a.name_; 
    return os; 
} 


int main() { 

    A com("com"); 

    std::cout << com << std::endl; 
    std::cout << com.example << std::endl; 
    std::cout << com.example.shape << std::endl; 
    std::cout << com.example.shape.triangle << std::endl; 
    std::cout << com.example.shape.triangle.equilateral << std::endl; 
    std::cout << com.example.shape.triangle.isosceles << std::endl; 
    std::cout << com.example.shape.triangle.right << std::endl; 
    std::cout << com.example.shape.quadrilateral << std::endl; 
    std::cout << com.example.shape.quadrilateral.rectangle << std::endl; 
    std::cout << com.example.shape.quadrilateral.squere << std::endl; 

    return 0; 
} 

Es ist schön, zu verwenden, insbesondere IDE mit mit Code-Hinweisen, aber leider nicht einfach zu definieren. Jede andere Baum-Ebene erfordert, dass eine neue Klasse mit neuen Mitgliedern definiert wird, dieser Name entspricht einigen Zeichenfolgen.

Ich bin auf der Suche nach einer besseren Lösung - einfacher. Es wäre toll, wenn es wie eine Art Template-Spezialisierung definiert wäre, aber ich weiß nicht, wie ich es machen soll.

Alle Vorschläge willkommen :) Grüße, Piciu.

+1

Ich nehme an, dies ist nicht nur eine komplizierte Art, Zeichenfolgen zu drucken. Wozu dient diese Namenshierarchie eigentlich? Es würde helfen, das zugrundeliegende Problem besser zu verstehen, anstatt nur Ihren Versuch an einem ganz bestimmten Teil der Lösung zu sehen, wie Sie es sehen – Smeeheey

Antwort

0

Dies ist ein etwas anderer Ansatz.

Hier erstellen wir Variable name_token s passend zu Ihren com Namen. Wir stiften sie dann zusammen mit einem intelligenten operator/. erste

Es ist ein bisschen von vorformulierten, aber die eigentliche Grammatik ist prägnant:

template<class...Ts> 
struct or_trait : std::false_type {}; 
template<class T0, class...Ts> 
struct or_trait<T0, Ts...> : std::integral_constant<bool, T0{} || or_trait<Ts...>{} > {}; 

    namespace names { 
    template<class Tag, bool is_root, class...Parents> 
    struct name_token { 
     std::string name; 
     name_token(std::string in):name(std::move(in)) {} 

     template<class Rhs> 
     constexpr static bool is_valid() { 
     return or_trait< std::is_same<Parents, Rhs>... >{}; 
     } 
    }; 

    template<class Tag, class...Parents> 
    name_token<Tag, false, Parents...> name(Tag, std::string s, Parents const&...) { return std::move(s); } 

    template<class Tag, class...Parents> 
    struct name_token<Tag, true, Parents...> { 
     std::string name; 
     name_token(std::string in):name(std::move(in)) {} 

     operator std::string() const { return name; } 

     template<class Rhs> 
     constexpr static bool is_valid() { 
     return or_trait< std::is_same<Parents, Rhs>... >{}; 
     } 
     friend std::ostream& operator<<(std::ostream& os, name_token const& self) { 
     return os << std::string(self); 
     } 
    }; 

    template<class Tag, class...Parents> 
    name_token<Tag, true, Parents...> rootname(Tag, std::string s, Parents const&...) { return std::move(s); } 


    template<class LastToken> 
    struct name_expression { 
     std::string current; 
     operator std::string()const { return current; } 

     friend std::ostream& operator<<(std::ostream& os, name_expression const& self) { 
     return os << std::string(self); 
     } 

     template<class Rhs> 
     constexpr static bool is_valid() { 
     return LastToken::template is_valid<Rhs>(); 
     } 
    }; 

    template<class Lhs, class Rhs, 
     std::enable_if_t< Rhs::template is_valid<Lhs>(), int>* =nullptr 
    > 
    auto operator/(Lhs lhs, Rhs rhs) { 
     return name_expression<Rhs>{ std::string(lhs) + "." + rhs.name }; 
    } 

    template<class Lhs, class Rhs, 
     std::enable_if_t< Rhs::template is_valid<Lhs>(), int>* =nullptr 
    > 
    auto operator/(name_expression<Lhs> lhs, Rhs rhs) { 
     return name_expression<Rhs>{ std::string(lhs) + "." + rhs.name }; 
    } 

    template<class Tag, class Token> 
    struct uniquely_tagged { 
     Tag tag; 
     Token const& token; 
     auto operator*(std::string n)&& { 
      return name(tag, std::move(n), token); 
     } 
    }; 
    template<class OldTag, bool b, class...Parents, class Tag> 
    auto operator*(name_token<OldTag, b,Parents...> const& parent, Tag tag) { 
     return uniquely_tagged<Tag, name_token<OldTag, b,Parents...>>{tag, parent}; 
    } 
    } 

Wir haben dann den Namen Token erstellen kann wie folgt:

auto com = names::rootname([]{}, "com"); 
auto example = com*[]{}*"example"; 
auto shape = example*[]{}*"shape"; 
auto triangle = shape*[]{}*"triangle"; 
auto right = triangle*[]{}*"right"; 
auto isosceles = triangle*[]{}*"isosceles"; 
auto equilateral = triangle*[]{}*"equilateral"; 


auto quadrilateral = shape*[]{}*"quadrilateral"; 
auto rectangle = quadrilateral*[]{}*"rectangle"; 
auto square = quadrilateral*[]{}*"square"; 

Jetzt com/example funktioniert, aber com/shape erzeugt ein Fehler bei der Kompilierung

Live example.

Bit hässlich, nicht dieses verwenden.

+0

Hallo, sieht wirklich gut aus. Die Verwendung ist etwas weniger komfortabel (hat keine IDE-Unterstützung, wie für Mitglieder), aber ein Weg besser zu definieren. Es ist erstaunlich, dass das Schreiben dieses Codes weniger Zeit gekostet hat als ich es zu verstehen :) – Piciu