0

edit: the linked one doesn't solve this particular case, since in my case there is a nested type.

Lets imagine the situation is like this:

//foo.hpp
//include guards

template <typename T>
class foo
{
    class bar;
};

The code for bar is too big, so I'd like (not really me, but it doesn't matter) to another file.

So, the current situation is this:

//foo.hpp
#include <bar.hpp>
//include guards

template <typename T>
class foo
{
     class bar;
};

//bar.hpp
#include <foo.hpp>
//include guards    

template <typename T>
class foo::bar
{
    //...
}

This is certainly circular dependency. But the thing is that compiler (clang and gcc) accepts it.

Should I fix it or there is nothing to fix? If it is an error, how should I fix it? Should I just dump everything into one file and call it a day?

In reality, that bar is an iterator, which I wouldn't want to make standalone.

There are include guards there, so I guess that is what prevents it from being included many times, but still the circular dependency makes me worried.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Incomputable
  • 2,188
  • 1
  • 20
  • 40
  • @xaxxon, the context is that the inner class is an iterator, which I wouldn't want to make standalone. I'll add that to the post. – Incomputable Jun 23 '17 at 16:49
  • There is no circular dependency as `class bar;` in `foo` is just a declaration. – Jean-Baptiste Yunès Jun 23 '17 at 16:53
  • This type of out-of-line definition is common in how you create a pimpl implementation class. As far as the header guards go, you can simply hand-trace the preprocessor process to see the order that the compiler would see things. – xaxxon Jun 23 '17 at 16:54

1 Answers1

1

Well, at first, you cannot use bar without foo, for sure... But you want to get it out of foo's header. So what about this:

foo.hpp:

//include guards

// no, we won't include bar.hpp here...

template <typename T>
class foo
{
    class bar;
};

// foo::bar is now predeclared

// and now we include bar:
#include "bar.hpp"

bar.hpp is not intended to be included directly, so we just leave the inclusion of foo:

//include guards    

// don't need it:
//#include "foo.hpp"


template <typename T>
class foo::bar
{
    //...
};

Anyone including foo will include bar, too. To denote more clearly that bar is not intended to be included directly, I recommend moving it to a different folder with some appropriate name, e. g. "private".

Unfortunately, we might lose IDE support for foo within bar.hpp. Actually, we can simply get it back by indeed including foo.hpp within bar.hpp.

But why - then we have a circular include again???

Well, yes, we have, but the circle is broken! Remember, bar.hpp is included after foo's declaration! That is the big difference here compared to normal circular inclusion. So what happens, seen from the compiler:

  • foo.hpp is included somewhere, foo is declared, bar.hpp is added afterwards.
  • foo.hpp is included again, but the include guards are defined, the circle is broken.

Seen from the IDE it is just vice versa: * bar.hpp includes foo.hpp * foo.hpp includes bar.hpp again, but the guards are defined already.

So we are fine... Actually, one could now even include bar.hpp and still would get foo with.

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • That is what I thought about as well, but inside of the bar header there will be 0 IDE support and other tools will be rendered useless. I'll take this as last resort. – Incomputable Jun 23 '17 at 16:55
  • @Incomputable So we need to trick the IDE... Haven't tested, but this might help (bar.hpp): `#ifndef FOO_H #include foo.h #endif` Possibly you need the same trick to prevent re-inclusion of bar.hpp in foo.hpp. – Aconcagua Jun 23 '17 at 17:00
  • that is gonna a lot of dancing around. I guess I'll just leave everything as is, and deal with it when problems arrive. If nobody posts more answers, I'll just accept yours. Would be great if you could add note about IDE. – Incomputable Jun 23 '17 at 17:02
  • yeah, so there is no real problem. I'm not sure if this is by design of the language though. – Incomputable Jun 23 '17 at 17:28
  • @Incomputable That's how the preprocessor works. It simply replaces the include with the content of the file to be included and processes the result afterwards again (sure, true preprocessor certainly will do it more efficiently...). If a file is included recursively, it won't care about, as long as recursion ends at a certain point. – Aconcagua Jun 23 '17 at 17:34