0

For teaching purposes in my applied oriented object courses, we are asked to develop a fully featured C++ application without using the STL nor any string manipulation functions from cstring (SDL for GUI will be involved in a later stage).

While redeveloping simple String and list container hierarchy classes, I encountered a cyclical dependency issue. Previously, I fixed these kind of issues using forward declaration. However, this time, things do not go as expected and this problem kept me busy for a few evenings now.

Here is a simple UML diagram of the issue I have.

UML diagram

Every class has their own .cpp and .hpp files, except for BaseListItemNotFoundException I declare with a using statement above the BaseList class declaration.

class BaseListItemNotFoundException: BaseException {
    using BaseException::BaseException;
};

Even if this doesn't add any added pieces of info (IMHO), let me precise BaseList and HeplList classes are actually template classes defined using .ipp and .hpp.

I omitted some other classes involved to restrict the environment to a minimal working example (iterator and Cell generic class hierarchy used as payload for the lists). Header protections using define and ifndef conditions have been removed for clarity.

Here are a snipped of the files:

BaseList.hpp:

#include <cstddef>
#include <iostream>
#include "Cell.hpp"

class HeplString; // Forward declaration

#include "BaseException.hpp"
class BaseListItemNotFoundException: BaseException {
    using BaseException::BaseException;
};

template<class T>
class BaseList {
    // code
};

HeplList.hpp:

#include <cstddef>
#include "BaseList.hpp"
#include "Cell.hpp"

template<class T>
class HeplList : public BaseList<T> {
    // code
};

#include "HeplList.ipp"

HeplString.hpp:

#include <cstddef>
#include <iostream>
#include <ostream>
#include <fstream>
#include "HeplList.hpp"

class HeplString {
    // code
};

BaseException.hpp:

#include "HeplString.hpp"
#include "BaseList.hpp"

class BaseException {
    // code
};

The main issue I have with this example is errors like this one:

src/tests/../BaseException.hpp:9:20: error: field ‘msg’ has incomplete type ‘HeplString’
         HeplString msg;
                    ^~~
In file included from src/tests/../HeplList.hpp:5,
                 from src/tests/../HeplString.hpp:9,
                 from src/tests/test.cpp:2:
src/tests/../BaseList.hpp:9:7: note: forward declaration of ‘class HeplString’
 class HeplString;
       ^~~~~~~~~~

I don't understand what I'm doing wrong here. Reading other similar issues didn't help.

My git repository with the full code is available here, if needed: https://github.com/wget/hepl-2-cpp

wget
  • 195
  • 2
  • 15
  • 1
    Why does `BaseException` need to know about `BaseList` and `HeplString`? – Mooing Duck Dec 05 '18 at 19:45
  • `BaseException` is internally using an `HeplString` in order to store the custom error message. Therefore, `BaseException` is implicitly making use of `BaseList` via the use of `HeplString`. – wget Dec 05 '18 at 19:48
  • 2
    You seem to come full circle here. The ring *must* be broken somewhere. You can never create a data member with only a forward declaration, but you *can* create a pointer or reference to it. This means the instance will have to live somewhere else though. – ravnsgaard Dec 05 '18 at 19:56
  • @ravnsgaard Using a reference is something I have seen as well among the proposed solutions, but with the current use case, I don't know how I could implement this in an elegant OOP manner :-/ – wget Dec 05 '18 at 20:07
  • @wget Hmm... Untested, but... `BaseException` *needs* `HeplString`, so you cannot just forward declare that, but `HeplString` can make do with a forward declaration of `HeplList`. You are only returning it from one member function; that only requires a declaration, not a definition. – ravnsgaard Dec 05 '18 at 20:23
  • Hint: The definition of `HeplString` _class_ probably doesn't require the definition of anything else. Only it's method definitions. That class can be your root. – Mooing Duck Dec 05 '18 at 21:21
  • @ravnsgaard Unfortunately I have an explode() method in my class HeplString which manipulates the HeplList object directly, so I cannot even not forward declare it. I followed this example to understand better the situation: https://stackoverflow.com/a/553869/3514658 – wget Dec 11 '18 at 15:27
  • @MooingDuck same explanation as above. I'm manipulating `HeplList`, so I do actually require inclusion. How can I solve this? I tried to break the loop by using references, but this is not working. Also subclassing `HeplString` and craft a class called `HeplStringExploded` to replace `HeplList` doesn't help either (unless I'm proceeding badly here). – wget Dec 11 '18 at 15:30
  • 1
    You need to keep your declarations and definitions clearly separated. The `HeplString` *declaration* only needs a forward declaration of `HeplList`; the *definition* needs the full definition of `HeplList`. – ravnsgaard Dec 11 '18 at 16:09
  • @ravnsgaard Like I said, this is exactly what I did :). I removed `#include "HeplList.hpp"` from `HeplString.hpp` and replaced it by the forward declaration `template class HeplList;`, but the `HeplString.cpp` complains with these error: https://gist.github.com/wget/2d9d156953e3e29abd94e023c54a0a98 – wget Dec 14 '18 at 12:49
  • @ravnsgaard These are the two files concerned by this issue: https://github.com/wget/hepl-2-cpp/blob/master/src/HeplList.hpp and https://github.com/wget/hepl-2-cpp/blob/master/src/HeplString.hpp – wget Dec 14 '18 at 12:51
  • @ravnsguard Thanks for your latest comment. Rethinking to that sentence after a good night put me on track. :) – wget Dec 18 '18 at 14:34

1 Answers1

1
  • Add #include "BaseException.hpp" to BaseList.hpp
  • Add #include "HeplList.hpp" to HeplString.cpp
  • Add the forward declaration template<class T> class HeplList; to HeplString.hpp
  • Now, you may need to modify some of your other classes which weren't including the BaseList.hpp header because they were relying on the header HeplString.hpp to do this for them.
wget
  • 195
  • 2
  • 15
  • The commit that fixed my issue: https://github.com/wget/hepl-2-cpp/commit/ac144652c5e0146f6dce1294503dfeee16812e6c – wget Dec 18 '18 at 14:31