Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop propagating declarations through hierarchical includes?

Whenever I make a .h header file, a question comes to my mind: "How to stop propagating declarations through hierarchical includes?" Assume there are these below files:

foo.h

#ifndef FOO_H
#define FOO_H

typedef int foo_t;

inline int foo() { return 1; }

class foo_c {};

#endif  /* FOO_H */

bar.h

#ifndef BAR_H
#define BAR_H

#include "Foo.h"

typedef foo_t bar_t;

inline int bar() { return foo(); }

class bar_c : public foo_c {};

#endif  /* BAR_H */

zoo.h

#ifndef ZOO_H
#define ZOO_H

#include "Bar.h"

typedef bar_t zoo_t;

inline int zoo() { return bar(); }

class zoo_c : public bar_c {};

#endif  /* ZOO_H */

In file zoo.h, we can access declared elements foo_c, foo_t, foo(), and every change to foo.h will re-compile zoo.h

I know we can move implementations to .cpp files, but how about the codes written in class definitions in .h files? How can we force the programmer to explicitly include foo.h in zoo.h if he needs it?

As an example in Qt, when I include and use <QQueue>, I have no access to QList where QQueue is inherited by QList and I have to include <QList> explicitly. (Also, I dont know how it is done, and effect of it on compile time)

like image 873
masoud Avatar asked Dec 06 '25 10:12

masoud


2 Answers

In C++ and C, "to stop propagating declarations" you need to remove them from public interface, period. Move them to implementation. Or to "less public" interface.

Compilation time is one of goals. Others are portability, maintenability. Also this is directly related with loose coupling.

The most popular C++ technique that can help with your class derivation is Pimpl idiom. Derive your implementation class, include corresponding header into implementation cpp and forward-declare implementation in your public interface. Your users will know nothing about base class and will know only the name of your implementation.

It's not possible to stop propagation if you'd like to use typedef's. But to provide better portability and maintenability you can use the same approach as Boost libraries use effectively: implementation-defined type (e.g. this one).

Each interface design is a tradeoff between extensibility, information hiding and simplicity (or effort). If you need to archive first two use more sophisticated approach. You can provide two public interfaces: one for usage and another one, much wider and lower-level, for extensibility.

like image 126
Andriy Tylychko Avatar answered Dec 08 '25 00:12

Andriy Tylychko


I find it important to clearly separate forward-declarations vs definitions in my code: use forward-declarations as much as possible.

In general, if your class X does not need to know the sizeof class Y, all you need is a forward-declaration of Y - you do not need to include Y.hpp.

For example, if X does not subclass from Y and X does not contain any members of type Y, then you don't need to include Y.hpp. Forward-declaring class Y; is sufficient. Sometimes, to decouple my code better, I will hold a reference or pointer to Y rather than embed Y in class X - if this is feasible, again, all I need to do is forward-declare class Y;

Now, there is a comment about not being able to forward-declare when you use template classes. But there is a trick around this - instead of using typedef, subclass from the template instantiation that you want eg:

class Bars : public std::vector<Bar> { };

Now you can forward-declare class Bars;, where previously you could not forward declare std::vector<Bar>;

So, these are the steps I follow in all my C++ projects:

  1. separate my code into modules divided by namespaces
  2. create a fdecl.hpp file in each module that contains forward declarations for that module
  3. strongly prefer the use of #include <modulename/fdecl.hpp> over any #include <modulename/foo.hpp> (forward declarations over definitions)

In this way, the headers are loosely coupled and I get faster compile times as I modify code.

like image 25
kfmfe04 Avatar answered Dec 08 '25 00:12

kfmfe04



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!