2017-04-05 97 views
1

我相信這是一個壞主意。假設我有一個很好的理由去做。我有一個成功使用靜態多態傳遞消息的節點樹。關鍵的是,每個節點不能連接到的節點類型,它只知道它傳遞的消息類型。爲了遍歷樹,我使用CRTP實現了訪問者模式。這適用於樹的第一級。混合雙派遣和靜態多態性

但是,當遍歷樹的第二層時,使用下面的AnyNode類刪除下一個節點的類型。我一直無法弄清楚如何從刪除類型轉換爲具體類型。下面的例子在測試中起作用,但我認爲這也可能是非常危險的,只是在內存恰好佈局的地方運氣。

似乎有一個問題,我必須在中刪除訪問者的類型,這在AnyNode::Concept::accept中是完全已知的。但我無法弄清楚如何從概念模型概念(我嘗試了協變虛擬cast功能,但沒有工作)。而且我無法使用虛擬方法將類型訪問者傳遞給派生模型類,因爲虛擬方法無法進行模板化。

有沒有一種安全的方式可以撥打node.accept並傳遞訪問者而不必刪除訪問者的類型,然後靜態地將其返回?有什麼方法可以在運行時將Concept降級到Model<T>?有沒有更好的方法來解決這個問題?是不是有一些瘋狂的新的C + + 11的方式來解決這個問題,可能與SFINAE?這裏

class AnyNode 
{ 
    struct Concept 
    { 
     virtual ~Concept() = default; 

     template< typename V > 
     void accept(V & visitor) 
     { 
      acceptDispatch(&visitor); 
     } 

     virtual void acceptDispatch(VisitorBase *) = 0; 
    }; 

    template< typename T > 
    struct Model : public Concept 
    { 
     Model(T &n) : node(n) {} 

     void acceptDispatch(VisitorBase * v) override 
     { 
      // dynamic cast doesn't work, probably for good reason 
      NodeVisitor<T>* visitor = static_cast< NodeVisitor<T>* >(v); 
      std::cout << "CAST" << std::endl; 
      if (visitor) { 
       std::cout << "WAHOO" << std::endl; 
       node.accept(*visitor); 
      } 
     } 

    private: 
     T &node; 
    }; 

    std::unique_ptr<Concept> mConcept; 
public: 

    template< typename T > 
    AnyNode(T &node) : 
      mConcept(new Model<T>(node)) {} 


    template< typename V > 
    void accept(V & visitor) 
    { 
     mConcept->accept(visitor); 
    } 
}; 

編輯的訪問者基類,並舉例衍生遊客。派生的訪問者通過客戶端代碼實現(這是一個庫的一部分),所以基類不知道訪問者將實現什麼。我擔心這會分散中心問題,但希望它有助於解釋這個問題。除了在outlet_visitor::operator()的AnyNode指針上調用->accept(visitor)時,此處的所有內容都可以正常工作。

// Base class for anything that implements accept 
class Visitable 
{ 
public: 
}; 


// Base class for anything that implements visit 
class VisitorBase 
{ 
public: 
    virtual ~VisitorBase() = default; 
}; 

// Visitor template class 

template< typename... T > 
class Visitor; 

template< typename T > 
class Visitor<T> : public VisitorBase 
{ 
public: 
    virtual void visit(T &) = 0; 
}; 

template< typename T, typename... Ts > 
class Visitor< T, Ts... > : public Visitor<Ts...> 
{ 
public: 
    using Visitor<Ts...>::visit; 

    virtual void visit(T &) = 0; 
}; 

template< class ... T > 
class NodeVisitor : public Visitor<T...> 
{ 
public: 

}; 

// Implementation of Visitable for nodes 

template< class V > 
class VisitableNode : public Visitable 
{ 
    template< typename T > 
    struct outlet_visitor 
    { 
     T &visitor; 
     outlet_visitor(T &v) : visitor(v) {} 


     template< typename To > 
     void operator()(Outlet<To> &outlet) 
     { 
      for (auto &inlet : outlet.connections()) { 
       auto n = inlet.get().node(); 
       if (n != nullptr) { 
        // this is where the AnyNode is called, and where the 
        // main problem is 
        n->accept(visitor); 
       } 
      } 
     } 
    }; 

public: 
    VisitableNode() 
    { 
     auto &_this = static_cast< V & >(*this); 
     _this.each_in([&](auto &i) { 
      // This is where the AnyNode is stored on the inlet, 
      // so it can be retrieved by the `outlet_visitor` 
      i.setNode(*this); 
     }); 
    } 

    template< typename T > 
    void accept(T &visitor) 
    { 
     auto &_this = static_cast< V & >(*this); 
     std::cout << "VISITING " << _this.getLabel() << std::endl; 

     visitor.visit(_this); 

     // The outlets are a tuple, so we use a templated visitor which 
     // each_out calls on each member of the tuple using compile-time 
     // recursion. 
     outlet_visitor<T> ov(visitor); 
     _this.each_out(ov); 
    } 
}; 

// Example instantiation of `NodeVistor<T...>` 

class V : public NodeVisitor< Int_IONode, IntString_IONode > { 
public: 

    void visit(Int_IONode &n) { 
     cout << "Int_IONode " << n.getLabel() << endl; 
     visited.push_back(n.getLabel()); 
    } 

    void visit(IntString_IONode &n) { 
     cout << "IntString_IONode " << n.getLabel() << endl; 
     visited.push_back(n.getLabel()); 
    } 

    std::vector<std::string> visited; 
}; 
+0

爲什麼'dynamic_cast'沒有工作? – 1201ProgramAlarm

+0

是否有數量有限的模型或訪問者?他們可以在任何地方被枚舉嗎?什麼是「VisitorBase」?這些是3個問題,請全部回答3. – Yakk

+0

爲了回答您的兩個問題,我添加了周圍的代碼。我希望這不是TMI。 @ 1201ProgramAlarm我認爲dynamic_cast不起作用,因爲'NodeVisitor < T >'只是訪問者的類層次結構的一部分。 – Ian

回答

1

啊,我想我現在看到你的問題了。這裏dynamic_cast(和static_cast)的問題在於,具有多種類型的NodeVisitor不會生成所有單類型的Visitor類。

在您提供的例子中,類VNodeVisitor< Int_IONode, IntString_IONode > derrived,這將最終產生Visitor< Int_IONode, IntString_IONode >Visitor<IntString_IONode>類作爲基地。請注意,未生成Visitor<Int_IONode>。 (visit<Int_IONode>Visitor< Int_IONode, IntString_IONode >。)您也沒有NodeVisitor<Int_IONode>NodeVisitor<IntString_IONode>。將任何東西投射到任何一個類都將是Undefined Behavior,因爲您正在投射的類不能是其中之一。

爲了解決您需要生成所有單類型Visitor類。我覺得這樣的事情可能工作(注:未測試):

這將定義單一類型Visitor類中的所有visit方法。

接下來,改變visitoracceptDispatch

auto visitor = dynamic_cast< Visitor<T>* >(v); 

由於vVisitorBase,如果一切正常宣稱這應該得到你想要的Visitor類和含visit方法。

+0

太棒了!這很有效,另外'class Visitor < T >'需要使用虛擬繼承('virtual public VirtualBase')從'VirtualBase'繼承,以避免死亡。 – Ian

0

不,這是不可能的。

假設您有3個模塊。模塊1是你的圖書館。模塊2定義了一個節點類型。模塊3定義了一個訪問者。

它們分別編譯爲二進制動態庫,然後在運行時加載。

如果訪問者知道節點類型的完整類型,它將能夠對節點類型的屬性進行任意的編譯時檢查,確實會改變其行爲方式。例如,它在編譯時檢查靜態node_type::value是否編碼「P = NP」的證明。

同時,節點類型DLL中沒有人使用node_type::value,所以編譯器在那裏存在它的存在(非常有效)。

要做到你的要求,你必須送不只是node_type編譯結果的事,但相當於node_type整個源visitor DLL,並在DLL中他們可以重新編譯他們的visitor針對此特定node_type

如果您放寬了一打隱含要求中的任何一個,這是可行的,但您已選擇了一組不兼容的要求。很可能你所要求的並不是你實際需要的東西,你只是想問一個非常普遍的要求,並指出它已經足夠了,然後就不明白爲什麼你不能這麼做。

+0

所有的模板都在標題中,所以這個工作。 – Ian