0

I have a ploblem about circular template reference. I want to make a tree using class node and class edge as following;

template <typename EdgeT>
class node
{
public:
    std::vector<EdgeT> edge_out;
    std::vector<EdgeT> edge_in;
};


template <typename NodeT>
class edge
{
public:
    NodeT* src;
    NodeT* dst;
    int weight;
};


template <typename NodeT, typename EdgeT>
class graph
{
public:
    std::vector<NodeT> nodes;
};

I found that I cannot declare graph class ex:

graph< node, edge > g; // <--- this cannot be solved 

graph< node< edge <node.....>, edge< node< edge>>  >  //it makes infinity declaration..

How can I redefine the structure of the classes?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
MooMoo
  • 1,086
  • 12
  • 22
  • Does it have to be templated? – Xymostech Mar 11 '13 at 01:59
  • "edge" and "node" class there should be the base classes. I plan to reuse them. ex an inherited edge can contain "flow" "reverse_flow" .... or an inherited "node" class can has more data such as flag, ..etc... so thats why i make them as templates – MooMoo Mar 11 '13 at 02:05
  • this isn't making any sense to me... how can a node hold an edge with a source and a destination? Isn't the source always the node itself? It should be holding a list of nodes... – user541686 Mar 11 '13 at 02:57
  • I treat "edge" as a pipe. when you pick up an edge, you should know where it is link to (pointer to node). And "node" as the container of "edges". – MooMoo Mar 11 '13 at 04:59

2 Answers2

3

Here is one approach:

#include <vector>

template<template<typename NodeT,typename T>class EdgeT, typename T=double>
struct Node {
   typedef Node<EdgeT,T> self_type;
   typedef EdgeT<self_type, T> edge_type;
   std::vector<edge_type> edge_out;
   std::vector<edge_type> edge_in;
   T data;
};

template<typename NodeT,typename T>
struct Edge {
   typedef NodeT node_type;
   node_type* src;
   node_type* dst;
   int weight;
};

template<typename NodeT, typename EdgeT=typename NodeT::edge_type>
struct graph {
   typedef NodeT node_type;
   typedef EdgeT edge_type;
   std::vector<NodeT> nodes;
};

int main() {
   typedef graph< Node<Edge> > graph_type;
   graph_type my_graph;
   my_graph.nodes.push_back( graph_type::node_type() );
   my_graph.nodes.push_back( graph_type::node_type() );
   my_graph.nodes.front().edge_out.push_back( {&my_graph.nodes[0], &my_graph.nodes[1], 1} );
   my_graph.nodes.back().edge_in.push_back( {&my_graph.nodes[0], &my_graph.nodes[1], 1} );
}

for another approach, you could look at how boost::variant handles recursive variants.

Another way to approach this would be more formal. C++ template metaprogramming is a functional language -- there are various techniques from functional programming in order to describe recursive structures without forward declaration that can be used.

I'm betting a fixed point combinator of some kind might work, but it is beyond me to figure out how. :)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

You need to figure out the reason why you need to use templates. If for example you want to have the data dynamic in an edge, you could use:

//foward declaration
template <typename T>
class node
{
std::vector<Edge<T> > edge_out;
std::vector<Edge<T> > edge_in; 
} 


template <typename T>
class edge
{
Node<T>* src;
Node<T>* dst;
T    weight
}


template <typename T>
class graph
{
std::vector<Node<T> > nodes;
}

Likewise if you want to have data that differs in your node. But in general it is better to figure out the reason for templates beforehand. I have looked at overly templatized production code and it is quite unmaintainable.

user1952500
  • 6,611
  • 3
  • 24
  • 37