Gotcha
Gotcha

Reputation: 392

Circular dependency with full type information

I have 2 classes, which are both included in variant. The variant is then used in both classes.

template <typename T>
struct Deferred
{
    typedef T value_type;
};

template <typename T>
struct DeferredContainer
{
    typedef typename T::value_type value_type;
};

class DictionaryObject;
class MixedArrayObject;

using DictionaryObjectPtr = Deferred < DictionaryObject >; // DeferredContainer<DictionaryObject>
using MixedArrayObjectPtr = Deferred < MixedArrayObject >; // DeferredContainer<MixedArrayObject>

typedef boost::variant <
    MixedArrayObjectPtr,
    DictionaryObjectPtr
> ContainerPtr;

class MixedArrayObject
{
public:
    typedef int value_type;

    inline void SetContainer(ContainerPtr obj) { _container = obj; }
    inline ContainerPtr GetContainer() const { return _container; }

private:
    ContainerPtr _container;
};

class DictionaryObject
{
public:
    typedef float value_type;

    inline void SetContainer(ContainerPtr obj) { _container = obj; }
    inline ContainerPtr GetContainer() const { return _container; }

private:
    ContainerPtr _container;
};

I don't know exactly how this works, but when I try to expose some properties on either array or dictionary via typedef (and use them in DeferredContainer), I receive error:

error C2602: 'DeferredContainer::value_type' is not a member of a base class of 'DeferredContainer'

Can I make this work somehow? Any other ideas?


Update 31.5, 4:09 PM.

I have reduced the problem to its minimum. Hope this finally gets the attention. The code.

After you switch the MixedArrayObjectPtr to be DeferredContainer, the code won't compile anymore.

Upvotes: 3

Views: 439

Answers (1)

Barry
Barry

Reputation: 303537

I would propose just reordering your code to make sure that all the definitions are in the order that they are used in. We'll have to use Object* instead of Object due to the need of variant to have complete types:

// main types
class Dictionary;
class Array;
template <typename T> struct DeferredContainer;

using DictionaryPtr = DeferredContainer<Dictionary>;
using ArrayPtr = DeferredContainer<Array>;

struct Object;

// now definitions of them
class Dictionary
{
public:
     typedef int value_type;
     std::map<Name, Object*> list; // note Object*
};

class Array
{
public:
     typedef int value_type;
     std::vector<Object*> list; // note only one type here
};

template <typename T>
struct DeferredContainer
{
    // now can do stuff with T::value_type
};

And after all of that, we can declare Object. We cannot forward-declare an alias unfortunately, but we can just wrap up the variant:

struct Object {
    boost::variant<
        DictionaryPtr,
        ArrayPtr
    > value;
};

With that ordering, everything compiles.

Upvotes: 3

Related Questions