8

How can I solve the following circular dependency?

typedef boost::variant<int, bool, double, std::string, Structure> Value;
typedef std::list<std::pair<std::string, ValueContainer>> Structure;
typedef std::vector<Value> ValueContainer;

I'm attempting to represent objects from a C api database library in a more C++ form. This database allows one to store values or arrays of values, as well as having a representation for Structures, as follows:

typedef struct ApiStructureMember 
{
    char* name;
    struct ApiValueContainer value;
    struct ApiStructureMember_T* next;
} ApiStructureMember_T;

Finally, a union is used to represent a value, as follows:

typedef struct ApiValue 
{
    union 
    {
        int i;
        const char*   s;
        ...
        struct ApiStructureMember_T* firstStructureMember;
    } value;
} ApiValue_T; 
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Baz
  • 12,713
  • 38
  • 145
  • 268
  • Can you describe what more general problem you're trying to solve with this scheme? Or clarify if you intend this more as a pedantic exercise? – chase Aug 09 '12 at 13:55
  • [`make_recursive_variant`](http://www.boost.org/doc/libs/1_50_0/doc/html/variant/tutorial.html#variant.tutorial.recursive.recursive-variant) should help, somehow. – Xeo Aug 09 '12 at 14:19

2 Answers2

5

You can't have types mutually containing each other. Think about it: the compiler would get into an infinite loop generating the data.

There are two general patterns: the first is through data types pointing to each other

struct OddNode; // forward declaration

struct EvenNode 
{
    OddNode* prev;
    OddNode* next;
};

struct OddNode
{
    EvenNode* prev;
    EvenNode* next;
};

If you leave away the pointers, and implement the Even and Odd nodes with containment by value, then the compiler can never resolve the definitions.

In general: just draw a picture of how you want to lay out the data, with boxes representing data and lines connecting the various parts. Then replace each box with a class, and each line with a pointer to the corresponding class. If you have a circular dependency somewhere, simply forward declare the classes at the top of your code.

The second circular dependency is through the Curiously Recurring Template Parameter (CRTP)

template<typename T> 
struct Y 
{};

struct X
: 
    public Y<X> 
{};

Your example would be using the CRTP with Structure as X and std::list< ..., Structure> as Y<X>.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
2

Isn't this a solution?

class Structure;
typedef boost::variant<int, bool, double, std::string, Structure> Value;
typedef std::vector<Value> ValueContainer;

class Structure: public std::list<std::pair<std::string, ValueContainer>>
{
};
Baz
  • 12,713
  • 38
  • 145
  • 268