CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 9 of 9
  1. #1
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    [RESOLVED] Will this header only strategy, avoid circular inclusion?

    I know the basic rules about header-only libraries (such as functions must be declared inline, etc..) but when I tried creating a complicated engine, I was encountering circular inclusion problems. The reason was that I was defining and implementing each class in each own .h file. So if I had two classes which both of them needed to include and use the functionallities of the other, I had circular inclusion.

    This is the solution I thought of:

    1) For each class, I'm creating two files, a .h and a .hpp file (both of them are header files).
    2) The .h files contain only the specification of the class and forward declarations to other classes (I have to heap allocate them). The rule is to not include any other header files inside .h files, only standard libraries.
    3) The .hpp files include their corresponding .h file and implement the functionalities.
    4) .hpp's are included only once by the client with the help of an initialization header file (which include all the .hpp files only once).

    I believe this will solve the circular inclusion problem.

    An Example:

    a.h
    Code:
    #ifndef A_h
    #define A_h
    #include <iostream>
    
    class B;
    
    class A
    {
        public:
        A();
        void whatssup();
        inline setB(B *b) {this->b = b;}
    
        private:
        B *b;
    };
    
    #endif

    a.hpp
    Code:
    #ifndef A_hpp
    #define A_hpp
    #include "a.h"
    #include "b.h"
    
    A::A()
    {
    }
    
    A::whatssup()
    {
        std::cout << "whatssup" << std::endl;
    }
    
    #endif

    b.h
    Code:
    #ifndef B_h
    #define B_h
    
    class A;
    
    class B
    {
        public:
        B(A *a);
    
        private:
        A *a;
    };
    
    #endif

    b.hpp
    Code:
    #ifndef B_hpp
    #define B_hpp
    #include "b.h"
    #include "a.h"
    
    B::B(A *a)
    : a(a)
    {
        a->whatssup();
    }
    
    #endif

    Engine.h
    Code:
    #ifndef engine_h
    #define engine_h
    #include "a.hpp"
    #include "b.hpp"
    #endif

    Client.cpp:
    Code:
    #include <Engine.h>
    
    A *a = new A();
    B *b = new B(a);
    a->setB(b);
    So what is your thought? Let me know.
    Last edited by babaliaris; August 30th, 2019 at 01:15 PM.

  2. #2
    Join Date
    Jan 2015
    Posts
    16

    Re: Will this header only strategy, avoid circular inclusion?

    So what is your thought?
    Well unless you're dealing with templates you shouldn't have executable code inside header files. Definitions belong in source files not #include files.

    By the way did you try compiling that code?

  3. #3
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by jlb1 View Post
    By the way did you try compiling that code?
    No. But I've done something similar and much more complicated and works pretty good.

    Quote Originally Posted by jlb1 View Post
    Well unless you're dealing with templates you shouldn't have executable code inside header files. Definitions belong in source files not #include files.
    Well, probably you don't know about header-only libraries. This has some really good advantages when It comes to distributing a library that is ment to be used by others.

  4. #4
    Join Date
    Feb 2017
    Posts
    677

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by babaliaris View Post
    So what is your thought?
    Circular inclusion problems can often be resolved by a design change because they indicate tight coupling (like you have between the A and B classes). A first step in my view is to get rid of the coupling if possible because it both improves the design and removes the circular inclusion.

    Maybe A and B conceptually work better as one class AB?
    Maybe it's possible to change A and B so they no longer depend on each other but on another third class C (that is independent of both A and B)?

    If no one of these options are possible you can always keep both A and B on the same include file. If they strongly depend on each other why not keep them close together?

    Finally you declare one method in A inline,

    Code:
        inline setB(B *b) {this->b = b;}
    It's not necessary to do that, it will be inlined automatically. It's only free functions you need to declare inline in a header-only situation.
    Last edited by wolle; August 31st, 2019 at 02:37 AM.

  5. #5
    Join Date
    Feb 2017
    Posts
    677

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by jlb1 View Post
    Well unless you're dealing with templates you shouldn't have executable code inside header files. Definitions belong in source files not #include files.
    Who says that? The C++ standard doesn't. It's just an old convention that makes little sense today.

  6. #6
    2kaud's Avatar
    2kaud is offline Super Moderator Power Poster
    Join Date
    Dec 2012
    Location
    England
    Posts
    7,822

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by wolle View Post
    Who says that? The C++ standard doesn't. It's just an old convention that makes little sense today.
    Agreed. There is no language reason why you can't have executable code (or anything else for that matter) within header files. Also, these shouldn't really be called header files but include files. #include can be used anywhere in the code - not just at the top of a program and can contain any text that is wished to be included at the point of the #include statement. Also, the referenced file can have any extension - not just .h/.hpp (or .c or .cpp) - but .xyz if you want!
    All advice is offered in good faith only. All my code is tested (unless stated explicitly otherwise) with the latest version of Microsoft Visual Studio (using the supported features of the latest standard) and is offered as examples only - not as production quality. I cannot offer advice regarding any other c/c++ compiler/IDE or incompatibilities with VS. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/ and can be used without reference or acknowledgement. Also note that I only provide advice and guidance via the forums - and not via private messages!

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  7. #7
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by wolle View Post
    Circular inclusion problems can often be resolved by a design change because they indicate tight coupling (like you have between the A and B classes). A first step in my view is to get rid of the coupling if possible because it both improves the design and removes the circular inclusion.

    Maybe A and B conceptually work better as one class AB?
    Maybe it's possible to change A and B so they no longer depend on each other but on another third class C (that is independent of both A and B)?

    If no one of these options are possible you can always keep both A and B on the same include file. If they strongly depend on each other why not keep them close together?

    Finally you declare one method in A inline,

    Code:
        inline setB(B *b) {this->b = b;}
    It's not necessary to do that, it will be inlined automatically. It's only free functions you need to declare inline in a header-only situation.
    These was some really good advices! I never thought of them. I will keep in mind creating a 3d middle class that might help solving circular inclusion.

    The situation where you define them inside the same file, sounds really good! But this needs some good planning before coding, because you need to know the relations between classses. If you instantly dive into coding, you never know what relations might come up in the future.
    Last edited by babaliaris; August 31st, 2019 at 05:54 AM.

  8. #8
    Join Date
    Feb 2017
    Posts
    677

    Re: Will this header only strategy, avoid circular inclusion?

    Quote Originally Posted by babaliaris View Post
    I will keep in mind creating a 3d middle class that might help solving circular inclusion.
    This is kind of the standard way to break up circularly dependent classes. Also many so called design patterns in OO have this purpose for example Mediator. Instead of communicating directly with each other objects communicate indirectly via a mediator without knowing the other objects even exist. It means objects can easily be added and removed without bothering the other objects.

    So by designing for loose coupling right from the start one reduces the risk for tight coupling problems later when things change.

    The situation where you define them inside the same file, sounds really good!
    In fact when you require this in the .hpp files,
    Code:
    #include "b.h"
    #include "a.h"
    it effectively is like a.h and b.h were on the same include file.

    The advantage in my view of actually having them on the same .h file is that
    - the complexity of the include scheme is reduced, and that
    - the circularity is contained in one physical place which makes maintenance easier.

    You'll notice this if you merge a.h and b.h into a common ab.h file. The circularity is no longer visible on the outside and does not require special care anymore.

    Good luck!
    Last edited by wolle; September 1st, 2019 at 12:51 AM.

  9. #9
    Join Date
    Jul 2017
    Location
    Greece
    Posts
    130

    Re: Will this header only strategy, avoid circular inclusion?

    @wolle thanks!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured