Detecting circular #includes

I came across a very useful tool for detecting circular dependencies while converting my project from many .ino files to use .h and .cpp files instead. I unfortunately created some circular #includes when I initially partitioned the code from my .ino files and this tool was very helpful in visualizing what I had done wrong and then improving my design.

It's a python script that can be run against your project folder to create an image like this example. I got it to run on OSX but since it's python code it's intended to run equally well on Linux and Windows.

The most common is within each included file to…

inside **

file.h**
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”-
#indef file_h
  #define file_h
#endif
( file.h stuff )
β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”-

thus any subsequent references to file.h will ignore the contents of the file

1 Like

Yes I've become familiar with Include Guards lately... more than I'd ever hoped to. :wink: I've been deep in .h hell the last few days. BTW I think you have the last two lines interchanged...the #endif comes after the file.h stuff otherwise it never would be skipped.

I prefer to use #pragma once since it's a single line, and isn't subject to coming up with a unique name and then being sure to perfectly type it two times in a row without any error checking by the preprocessor nor compiler.

And there are some circular dependencies that can creep in even if you have include guards. So having a dependency graph is good to have even if it's just to have a clear picture of the structure of your sketch.

That is a non-standard directive, although it is supported by many compilers.

Can you give an example? I can not think of any that break the more traditional, compliant approach to atomic inclusion.

Yes I've read that #pragma once is not in the standard but it is widely supported. And specifically it works fine in the Arduino IDE. As I mentioned it seems the better choice to me vs the ifndef kludge.

I did a quick google using "example of c++ circular include dependency" to get you an example and the first AI generated item seemed very relevant. Unfortunately I couldn't figure out how to get an actual link to those results. So if you search again hopefully you'll get something similar.

Here's what the text summary said: " In this example, class_a.h includes class_b.h, and class_b.hincludes class_a.h. When the compiler tries to process class_a.h, it encounters the include for class_b.h. It then starts processing class_b.h, which in turn includes class_a.h again. This creates an infinite loop, and the compiler will eventually report an error."

Does that still happen when you use #ifndef protection?

Yes it does. include guards did not resolve my stubborn issue and I had to resort to leaving out one include to break the circle and use a small file with a couple of forward references instead. If I revert to the circular includes I have the same problem with an undefined struct type, which is clearly in the code and works OK when the forward reference is used instead of the .h file.

The fil.h stuff must be inside the ifndef otherwise you include it all the time

#indef FILE_H
#define FILE_H

// The content of the .h goes here 

#endif

Probably not written right ? (Use a Class forward declaration in the .h if there are cross dependencies)

Yes it's undoubtedly my lack of experience with including .h files.

I'm not clear on what forward declaration I'd put where.

The main symptom was that a struct declaration was not being seen in a different .cpp file that needed it as the return type of a function. The relevant .h was included in the needy .h, and that .h was included in the matching .cpp

And this is all with code that worked fine as a collection of .ino files. I spent 3 days being very careful to not touch my code except to turn definitions into declarations while I wrangled includes. And for most of that time my code wouldn't compile with the same error message about the struct name not being a defined type.

I read a lot of advice (sometimes conflicting) about the proper way to write .h and .cpp files and finally came across some things pointing out the dangers of circular includes.

I solved the issue by removing an include in an unrelated file (breaking the circular include chain) that had the minimum include needs and substituted a simple .h file that had three simple forward declarations to fulfill its needs.

None of that was directly related to the originally unfindable struct type which instantly and magically became findable after several days of .h hell.

So now I have 8 sets of .h and .cpp files that are unwieldy to edit in the IDE. (8 .ino files barely fit in the tab bar. So I'm off to figure out how to get the IDE to allow me to open and edit my files if I put them into some kind of folder arrangement.

I don't think the IDE supports folders, unless they are in a library.

Hi Bob

Thanks. I was thinking about leveraging a /src/ folder in my sketch folder which apparently works for including and compiling, but it doesn't seem to work for editing. Those files are all dimmed when navigating to them using the File->Open menu item.

I'm also happy to put all the .h and .cpp files right in the sketch folder, but the IDE creates tabs for ALL of them and unfortunately those tabs can't be closed.

The IDE does seem to be able to open .h files for libraries via Go To Definition menu item and those tabs can be closed. But I haven't yet found a way to simply open my .h and .cpp files into tabs than can be closed so that I don't always have 17 tabs open making things hard to find.

I'd love to hear how others manage editing projects with even a modest number of source files.

Personally I use Eclipse with the Sloeber plugin for any non-trivial Arduino project.

1 Like

Based on that language, I'd say you have already made up your mind. No more debate needed.

plunk.

Hey, I'm open to using a kludge if it works better than what I'm currently doing. :wink:

But using a three line construct (split across two different places) requiring a non-error checked (latent error inducing) unique identifier is inherently less appealing than a consistent single line located at the top of the file.

But hey if that's what's required to make c++ work right I'm game to get down and dirty when necessary.

#pragma once

... just a one-liner

Yes that's what I use and the other poster was reacting to when I called the ifndef alternative a kludge.

So, we're 16+ post into this thread now. I really think a small, concrete example of such a case would be far more illustrative than all the hand waving.

Include guards don't help with circular definitions, they only avoid multiple declarations. Circular definitions can happen, particularly if you are refactoring code. I would usually factor out the dependency into a separate module, although that isn't always easy.

#pragma once may be a GCC extension, but I think it is better than dealing with many of the botched attempts I have seen at include guards.

1 Like

A link from my first post includes a discussion of that. My purpose in starting this thread was simply to share Pierre's cool dependency graphing tool. I'm barely conversant in getting .h files to work and reporting the symptoms I see. I'm not the one to lead a conversation about circular includes.

I'm also happy to send you an archive of my project which exhibits unexplained (to me) compile behavior. I fixed the symptom by breaking the circular includes and adding three forward references.

Yes this exercise has definitely highlighted that I should partition my code better. But I wanted to change the bare minimum until l could get my project to at least compile again. I tried to go from .ino files to .h & .cpp files incrementally but there was a point where having separate compilation broke so many references that until everything was in .h and .cpp files and cross included that I could even get a compile to complete.

Now that I'm compiling again I'm very open to some restructuring, and that's why I like that dependency graph so I can better see the forest for the trees.