views:

306

answers:

5

We have two classes:

template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix

and

template<typename T, typename Size>
class Iterator

Matrix should be able to return begin and end iterators and Iterator will keep a referrence to the Matrix to access the elements via it's interface. We don't want Iterator to depend on the internal storage of the Matrix to prevent coupling. How can we solve this cyclic dependency problem?

(The internal Storage class has the same template parameters as the Matrix class and the same access procedures as the Matrix itself)

A: 

Forward-declare the Matrix template before defining the Iterator template.

Mind you, you'll hit a brick wall when you realise that an Iterator<T, Size> can't refer to a Matrix<T, Size, Stack, Sparse>.

Marcelo Cantos
So, doesn't the Matrix need the definition of the Iterator to call the constructor of the Iterator template class?
TomWij
Yes, but you're only _forward-declaring_ it before the iterator. You _define_ it after.
Marcelo Cantos
@TomWij, yes it does the forward declaraction just says that "class `Matrix` will exist, so you can make pointers and references to it (but you can't yet call methods on it)". The full definition of `Matrix` comes after the full definition of `Iterator`. All of the method definitions come after that.
Ken Bloom
+1  A: 

You can forward-declare a template. It looks like this:

template<typename T> struct Foo;

template <typename T> struct Bar
{
    Foo<T>* foo;
};

template<typename T> struct Foo
{
        T value;
        Bar<T*> foobar;
};

void bla()
{
        Foo<int> grml;
}
Rudi
So, forward decaration just works if both classes are separated into their own HPP with include guards? Where should I place the include and forward declaration then?
TomWij
@TomWij: Either at the beginning of the respective header or in yet another header file, included by both. The last is arguably cleaner but also redundant.
Konrad Rudolph
+1  A: 

In order to iterate, iterators typically do need to know about the internal storage they iterate over - this coupling can usually not be avoided. Take a map iterator for example - it is going to have to know about the internal tree structure of the map in order for it to do its job.

anon
Accepted this answer because that's indeed how a iterator should be, in our design approach we would have wanted the freedom to be able to write a Matrix with the same interface but a quite different implementation to use the same iterator. But well, the person that writes that implementation should indeed be responsible for writing his own iterator.
TomWij
+1  A: 

First, forward declare the Matrix class. This allows the Iterator class to see the name of the Matrix class, and make pointers and references to it. (It doesn't allow the Iterator class to access member data yet or call member functions yet.)

template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix;

Then, define the Iterator class. All it can do at this point is hold references and pointers to the Matrix. (No access to Matrix's members yet.)

template<typename T, typename Size>
class Iterator{
   // don't define any function bodies in here
   //but do put all data members and prototypes in here
};

Then define the Matrix class (which can access Iterator members)

template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix{
   // don't define any function bodies in here
   //but do put all data members and prototypes in here
};

Then define the method bodies for each class. At this point, the methods of both classes can access each other's members. Usually, this part goes in the .cpp file, but for templates it belongs in the .h file.

template<typename T, typename Size, typename Stack, typename Sparse>
Matrix<T,Size,Stack,Sparse>::Matrix(){ /*...*/}

template<typename T, typename Size>
Iterator<T,Size>::Iterator(){ /*...*/ }
Ken Bloom
This sounds like something that would work, but can this be separated into multiple files?
TomWij
It could be separated but you'd have to #include all of the files in the right order, so it's probably not a good idea. If there weren't templates involved, you could put all of the class definitions in the .h file and all of the method definitions in the .cpp file (as is normal).
Ken Bloom
+1  A: 

Using a nested class might also be appropriate here, and it may cut down on the number of template parameters you need.

template<typename T, typename Size, typename Stack, typename Sparse>
class Matrix{
public:
   class Iterator{
       // define Iterator members here
   };
   // declare Matrix members here
}
Ken Bloom