Je suis sûr que c'est une mauvaise idée. Faisons comme si j'avais une bonne raison de le faire. J'ai un arbre de noeuds qui utilise avec succès le polymorphisme statique pour transmettre des messages. Fondamentalement, chaque nœud ne peut pas les types de nœuds auxquels il se connecte, il ne connaît que les types de messages qu'il transmet. Pour parcourir l'arborescence, j'ai implémenté le modèle de visiteur en utilisant CRTP. Cela fonctionne pour le premier niveau de l'arbre. Cependant, lors du passage à la deuxième couche de l'arborescence, le type du nœud suivant est effacé à l'aide de la classe AnyNode ci-dessous. J'ai été incapable de comprendre comment baisser du type effacé au type concret. L'exemple ci-dessous fonctionne dans les tests, mais je pense que c'est probablement aussi vraiment dangereux et que je travaille juste par la chance d'où la mémoire arrive à être présentée.Mélange double répartition et polymorphisme statique
Il semble problématique que je dois effacer le type du visiteur dans AnyNode::Model<T>::acceptDispatch
, qui est entièrement connu dans AnyNode::Concept::accept
. Mais je ne peux pas comprendre comment baisser du concept au modèle dans le concept (j'ai essayé une fonction virtuelle covariant cast
, mais cela n'a pas fonctionné). Et je ne peux pas passer le type de visiteur à la classe Model dérivée en utilisant une méthode virtuelle, car les méthodes virtuelles ne peuvent pas être modélisées.
Y at-il un moyen sûr d'appeler node.accept
et de passer le visiteur sans avoir à effacer le type du visiteur, puis statique le repousser? Existe-t-il un moyen de réduire le concept à Model<T>
lors de l'exécution? Y a-t-il une meilleure façon d'aborder ce problème? N'y a-t-il pas une nouvelle façon de résoudre ce problème, peut-être avec 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);
}
};
EDIT est ici les classes de base des visiteurs, et un exemple dérivé visiteur. Les visiteurs dérivés sont implémentés par le code client (cela fait partie d'une bibliothèque), donc les classes de base ne peuvent pas savoir quels visiteurs seront implémentés. J'ai peur que cela distrait de la question centrale, mais j'espère que cela aide à expliquer un peu le problème. Tout fonctionne ici, sauf lorsque ->accept(visitor)
est appelée sur le pointeur AnyNode dans outlet_visitor::operator()
.
// 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;
};
Pourquoi 'dynamic_cast' ne fonctionne-t-il pas? – 1201ProgramAlarm
Y a-t-il un nombre fini de modèles ou de visiteurs? Peuvent-ils être énumérés n'importe où? Qu'est-ce qu'un 'VisitorBase'? Ce sont 3 questions, s'il vous plaît répondre tous les 3. – Yakk
Dans l'espoir de répondre à vos deux questions, j'ai ajouté le code environnant. J'espère que ce n'est pas TMI. @ 1201ProgramAlarm Je crois que dynamic_cast ne fonctionne pas parce que 'NodeVisitor < T >' est seulement une partie de la hiérarchie des classes du visiteur. – Ian