2016-08-04 88 views
0

我有一個類型的樹層次結構,每個都由字符串來定義,這樣的事情:類型的層次結構與編譯類型檢查

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 

類型定義了動態參數的一些數據,可以在改變運行時,所以無法創建編譯時類型層次結構。因此,每個實體只是一個類型名稱(字符串)和參數列表,您可以隨時在系統中註冊一個新類型。儘管如此,很多類型都是預定義的,並且可以在系統啓動時註冊。爲了在運行時創建和預定義的數據具有相同的體驗,我使用這個動態表示。對於預定義的類型,我希望有一種機制可以在編譯時驗證類型名稱,並且我不想在每次必須使用代碼時將字符串直接放入代碼中,可以通過定義字符串常量表達式來解決它,但是它不是很好,像這樣:

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

所以我想到一個更好的方法。

另一種方法是讓這樣的事情:

#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; 
} 

這是很好用,尤其是具有代碼提示IDE,但遺憾的是不容易做定義。每個不同的樹級別都需要用新成員定義新類,該名稱與某些字符串相對應。

我正在尋找更好的解決方案 - 更簡單。將它定義爲某種模板專門化會很棒,但我不知道該怎麼做。

歡迎任何建議:) Regards, Piciu。

+1

我認爲這不僅僅是打印字符串的複雜方式。這個命名層次究竟是什麼?這將有助於更好地理解底層問題,而不僅僅是當你看到它在解決方案的特定部分看到你的嘗試 – Smeeheey

回答

0

這是一個稍微不同的方法。

這裏我們創建與您的com名稱相匹配的變量name_token。然後,我們用巧妙的operator/將它們組合在一起。

有一點樣板的第一,但實際的語法簡潔:

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}; 
    } 
    } 

然後,我們可以創建名稱標記如下:

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"; 

現在com/example作品,但com/shape產生編譯時錯誤。

Live example

有點醜,考慮不要用這個。

+0

嗨,看起來非常好。使用不太舒服(沒有IDE支持,就像成員一樣),但更好的定義方式。寫這段代碼讓你花費更少的時間,然後我理解它,這真是太神奇了:) – Piciu