Visitor so ok?



  • Hallo,

    ich benutze gerade zum ersten Mal Visitoren (wie in "Design Patterns" beschrieben). Dabei frage ich mich insbesondere, ob man die eigentliche Nutzung des Visitors kürzer machen kann als ich.

    Meine abstrakte Visitorklasse ist

    class Nd_Visitor
    {
    public:
    	virtual void visit(Rev_Nd* n) { }
    	virtual void visit(Rev_Tip_Nd* n) { }
    	virtual void visit(Rev_Intern_Nd* n) { }
    	virtual void visit(Rev_Coal_Nd* n) { }
    	virtual void visit(Rev_Rec_Nd* n) { }
    };
    

    Die konkrete Visitorklasse sieht so aus

    class To_Dot_Str_Visitor : public Nd_Visitor
    {
    private:
    	Dot_Para m_dot_para;
    	string m_str;
    public:
    	To_Dot_Str_Visitor(Dot_Para dot_para) : m_dot_para(dot_para) { }
    
    	void visit(Rev_Nd* n);
    	void visit(Rev_Tip_Nd* n);
    	void visit(Rev_Intern_Nd* n);
    	void visit(Rev_Coal_Nd* n);
    	void visit(Rev_Rec_Nd* n);
    
    	const Dot_Para& dot_para() const { return m_dot_para; }
    	operator string() { return m_str; }
    };
    

    Was mir nun seltsam erscheint ist, dass aus der bisherigen Zeile

    ss << node()->to_dot_string(dot_para);
    

    bei Verwendung meines Visitors drei werden:

    To_Dot_Str_Visitor vst(dot_para);
    	node()->accept(&vst);
    	ss << (string)vst;
    

    Geht das irgendwie kompakter oder ist das bei Visitoren halt so?



  • Denke schon dass das kompakter geht. Die 2. und 3. Zeile kann man auf jeden Fall zusammenfassen:

    template <class O, class V> typename V::result_type ApplyVisitor(V& v, O& o)
    {
        o.Accept(v);
        return v.Result();
    }
    
    std::string foo()
    {
        Object o;
        Visitor v(123);
        return ApplyVisitor(v, o);
    }
    

    Die Variante "ApplyVisitor(Visitor(123), o);" geht leider nicht (ausgenommen mit MSVC und anderen Compilern die sich diesbezüglich nicht an den Standard halten), da ApplyVisitor eine non-const Referenz auf den Visitor braucht, da "Accept()" vermutlich auch eine non-const Referenz brauchen wird. (Das alte "r-value und non-const-ref" Thema halt)
    Dies könnte man höchstens umgehen indem man der konkreten Visitor-Klasse eine ApplyTo Funktion spendiert:

    class ConcreteVisitor : public AbstractVisitor
    {
    public:
        typedef std::string result_type;
    
        template <class O> std::string ApplyTo(O& o)
        {
            o.Accept(*this);
            return m_result;
        }
    
        // implement AbstractVisitor...
        virtual void Visit(A& a) { ... }
        virtual void Visit(B& b) { ... }
        virtual void Visit(C& c) { ... }
    
    private:
        std::string m_result;
    };
    
    std::string foo()
    {
        Object o;
        return ConcreteVisitor(123).ApplyTo(o);
    }
    

    Ich weiss aber nicht wie oft man soetwas wirklich mit einem Visitor machen wird, da der konkrete Typ des Visitors normalerweise dort wo man ihn auf Objekte anwendet nicht bekannt ist (warum sonst würde man einen Visitor brauchen?). Das impliziert dass der Visitor nicht dort erzeugt wird wo er angewendet wird (sonst würde man ja den konkreten Typ kennen), und das wiederum bedeutet dass man sowieso schon einen Zeiger oder eine Referenz auf den Visitor hat den/die man dann an ApplyVisitor übergeben kann.

    Ich kann mir höchstens vorstellen dass es in Fällen Sinn machen kann wo man an einigen Stellen den Visitor braucht (weil der konkrete Typ nicht bekannt ist), an anderen Stellen der konkrete Typ aber bekannt ist, und man an diesen anderen Stellen den bestehenden Visitor-Code wiederverwenden möchte (weils ja keinen Sinn macht alles doppelt zu programmieren). In dem Fall würde es einem eine Zeile abnehmen. Weiss aber nicht ob das dafürsteht.

    ----

    Ich hoffe ich habe jetzt keinen allzugrossen Unfug geschreiben 🙂


Anmelden zum Antworten