Is there a Boost based solution to this circular reference problem

Description: I have a class hierarchy which unfortunatelly results in circular dependencies not allowed by C++. Is there any simple way to fix this? Please note, I have trivialized this problem for the purpose of illustration. The actual code that needs this is far more complex. I am quite perplexed, please help: Here is the code made as simple as I can: part.h ------ class Part { }; action.h -------- // The Action class is where the problem arises. It depends on an iterator in a class which is not yet defined. // The class cannot be defined before the Action class, because it depends on the definition of the Action class. class Action { public: typedef std::list<Part> PartsList; private: // I want a back "reference" to the iterator pointing to // the part for which this action applies. This is OK, I can just // typedef PartsList myself and don't need Whole for this PartsList::iterator iPart; // I also want a back "reference" all the way to the iterator // in the NameWholeMap for whose value this action applies // Here, I have a circular reference issue. // Note: Storing the key in the NameWholeMap to eliminate // this problem is not an option. I need a direct reference back // to the association in the map (map iterator), so as not to have // to look it up again. NamedWholes::NameWholeMap::iterator iNamedWhole; }; whole.h -------- // Whole is basically a container of Parts. Action needs an iterator to the list it contains. // Fortunatelly, because the value in this list (i.e. Part) does not depend on Action, I can just // type define the same list (i.e. PartsList) in the Action class and refer to its iterator there. class Whole { public: typedef std::list<Part> PartsList; typedef boost::optional<Action> OptionalAction; typedef std::vector<Action> Actions; void GetActionsOnSomePartsForThisWhole(Actions &actions); private: OptionalAction DecideOnAction() const; Parts m_parts; }; namedwholes.h ---------------- // This is the other half of our circular reference problem. This class has a function which gathers // actions on various parts contained in various wholes contained in its NameWholeMap. Those // actions would like to know directly which iterator in the NameWholeMap they were created // from, so as not to require a search by key later. // The problem is that this class must know how Action is defined and Aciton must know how // this class is defined, creating a circular reference. class NamedWholes { public: typedef std::map<std::string,std::Whole> NameWholeMap; typedef Whole::Actions Actions; void GetAllActions(Actions &actions); private: NameWholeMap m_nameWholeMap; };

On Wed, 16 Nov 2005 16:56:00 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote:
Description:
I have a class hierarchy which unfortunatelly results in circular dependencies not allowed by C++. Is there any simple way to fix this? Please note, I have trivialized this problem for the purpose of illustration. The actual code that needs this is far more complex. I am quite perplexed, please help:
Here is the code made as simple as I can:
[snip] I think you can forward declare Action ate the top and define it later. You end up with vector<Action> in arguments. I know that using standard classes with incomplete types is not guaranteed to work, but I'm not sure if mentioning it as a parameter is a 'use'. Maybe this thread should be moved to c.l.c++.moderated. Bruno

"Bruno Martínez" <br1@internet.com.uy> wrote in message news:op.s0c8nu06551aze@yoda...
On Wed, 16 Nov 2005 16:56:00 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote: I think you can forward declare Action ate the top and define it later. You end up with vector<Action> in arguments. I know that using standard classes with incomplete types is not guaranteed to work, but I'm not sure if mentioning it as a parameter is a 'use'.
Maybe this thread should be moved to c.l.c++.moderated.
Bruno
You cannot forward declare a type used as a value for vector. Mike

On Thu, 17 Nov 2005 12:32:59 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote:
"Bruno Martínez" <br1@internet.com.uy> wrote in message news:op.s0c8nu06551aze@yoda...
On Wed, 16 Nov 2005 16:56:00 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote: I think you can forward declare Action ate the top and define it later. You end up with vector<Action> in arguments. I know that using standard classes with incomplete types is not guaranteed to work, but I'm not sure if mentioning it as a parameter is a 'use'.
Maybe this thread should be moved to c.l.c++.moderated.
Bruno
You cannot forward declare a type used as a value for vector.
Yes, I know. However, not fully using such instantiation, as is the case, seems somewhat less wrong. Another way to solve the problem is using an auxiliar class, vectorAction, which is forward declared at the beginning. It's definition, class vectorAction { vectorAction(std::vector<Action>& ref) : value(ref) {} std::vector<Action>& value; }; is put as the last thing. All uses of vector<Action>& are then changed for vectorAction. Does this work? Bruno

"Bruno Martínez" <br1@internet.com.uy> wrote in message news:op.s0exlge3551aze@yoda...
On Thu, 17 Nov 2005 12:32:59 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote:
You cannot forward declare a type used as a value for vector.
Yes, I know. However, not fully using such instantiation, as is the case, seems somewhat less wrong.
Another way to solve the problem is using an auxiliar class, vectorAction, which is forward declared at the beginning. It's definition,
class vectorAction { vectorAction(std::vector<Action>& ref) : value(ref) {} std::vector<Action>& value; };
is put as the last thing. All uses of vector<Action>& are then changed for vectorAction. Does this work?
Now we're "cooking with gas." This may be the trick I need to get around it. By wrapping a vector of actions in another class, I can just forward declare that class and not have dependencies on actions or vectors of actions. Thanks, Michael Goldshteyn P.S. I am still interested in other alternative approaches to this, if anybody can come up with one.

Bruno Martínez <br1@internet.com.uy> writes:
On Thu, 17 Nov 2005 12:32:59 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote:
"Bruno Martínez" <br1@internet.com.uy> wrote in message news:op.s0c8nu06551aze@yoda...
On Wed, 16 Nov 2005 16:56:00 -0200, Michael Goldshteyn <mgoldshteyn@comcast.net> wrote: I think you can forward declare Action ate the top and define it later. You end up with vector<Action> in arguments. I know that using standard classes with incomplete types is not guaranteed to work, but I'm not sure if mentioning it as a parameter is a 'use'.
Maybe this thread should be moved to c.l.c++.moderated.
Bruno
You cannot forward declare a type used as a value for vector.
Yes, I know. However, not fully using such instantiation, as is the case, seems somewhat less wrong.
It's not a question of "fully using." If the vector type is instantiated before the parameter is fully defined, it's an error. Otherwise, you're OK. The forward declaration technique can be used legally in many scenarios. -- Dave Abrahams Boost Consulting www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ulkzlbiyw.fsf@boost-consulting.com...
It's not a question of "fully using." If the vector type is instantiated before the parameter is fully defined, it's an error. Otherwise, you're OK. The forward declaration technique can be used legally in many scenarios.
The problem arises when I need an iterator - a nested typedef within vector, which as I understand it, requires the vector's value type to be defined, so that the vector template class, of that type, can be instantiated. For example: ---------------------------------- Foo.h ---------------------------------- class Foo { ... }; ---------------------------------- FooAction.h ---------------------------------- #include "Foo.h" class FooAction { public: typedef std::list<Foo> FooList; private: // The following causes us to need class Foo to be fully defined and not just declared. FooList::iterator m_iFooList; }; ---------------------------------- FooContainer.h ---------------------------------- #include "Foo.h" // This is definitely needed #include "FooAction.h" // Is this really needed? class FooContainer { public: typedef std::list<Foo> FooList; typedef std::vector<FooAction> FooActionVector; void GetActionableFoos(FooActionVector &actions); private: FooList m_listOfFoos; };

class NamedWholes { public: typedef std::map<std::string,std::Whole> NameWholeMap; typedef Whole::Actions Actions;
void GetAllActions(Actions &actions);
private: NameWholeMap m_nameWholeMap; };
One solution that has come to mind is to remove any Action stuff from NamedWholes and move it to another class that works with Actions and NamedWholes. Although this would most likely solve the circular reference issue, it would also decouple an operation (i.e. a function) on NamedWholes from that class to a helper class, which doesn't seem very object oriented. I am interested in hearing other ideas. Michael Goldshteyn
participants (3)
-
Bruno Martínez
-
David Abrahams
-
Michael Goldshteyn