Author Topic: Catching all required include directories in C++  (Read 2432 times)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1014
Catching all required include directories in C++
« on: September 17, 2017, 04:10:32 PM »
Okay, so this should be the final question before finishing the Windows version of OP2Archive...

I'm on the final cycles of refactoring the code. I keep on catching missing include files that do not force the program to fail when compiling.

What I mean is the header file below depends on the standard library string class. ConsoleSettings.h also depends on string. So, if I do not include string in ConsoleList.h, the program will compile and run fine without any warnings.

However, if some day in the future ConsoleSettings.h eliminates its dependency on string, then ConsoleList longer compile. This would be an odd and unintended consequence of removing a dependency from ConsoleSettings.h. So to prevent something like this, I'm being careful to try and include string in ConsoleList.h and other files that use it even when they don't currently require it to compile.

It seems like I'm always finding one that I'm missing while refactoring. Is there some way to simplify this problem by having the compiler/IDE complain about missing includes in a file that are contained in another file? Or, am I just overthinking this and shouldn't worry so much about finding and listing all the includes since they are covered in another dependency?

This affects declaring namespaces in the same way.

At first when learning C++, dealing with this weird behavior of includes and using namespace statements caused me a lot of confusion. I've learned to recognize when they are causing problems now, but what a headache!

ConsoleList.h
Code: [Select]
#pragma once

#include "ArchiveConsoleListing.h"
#include "ConsoleSettings.h"
#include <string>

using namespace std;

class ConsoleList
{
public:
void listCommand(const ConsoleArgs& consoleArgs);

private:
ArchiveConsoleListing archiveConsoleListing;

void listArchiveContents(const string& filename);
void listAllArchivesInDirectory(const string& directory);
};

Thanks,
Brett

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Re: Catching all required include directories in C++
« Reply #1 on: September 18, 2017, 12:28:43 AM »
This is quite a problem with the preprocessor #include method of just pasting in the included file. A proper module system with reasonable namespacing, like other popular languages now use, would go a long way towards solving this. There is a design in the works for C++, but it hasn't arrived quite yet.

Visual Studio 2017 has started offering experimental support for the new module syntax:
C++ Modules in Visual Studio 2017
Similarly, gcc is developing support:
gcc C++ Modules
As is Clang:
Modules - Clang 6 Documentation


Heck, even JavaScript is getting a proper module system added now. The future is bright indeed.


As for current workarounds, there's not a whole lot you can do. Test code can help. Unit tests typically only test one file, so if you're just including one file, and if it's missing some #include, you should catch it when compiling the unit tests.

The best one off test I can think of, that doesn't involve writing unit tests, is to just pass each header file on it's own to the compiler, as if it was a source file, and see if it produces any errors. That should remove the context of the including .cpp file, which may have already included some dependencies. Though, this only catches order dependent issues, where the .cpp includes a dependency (either directly or indirectly) before a file that needs that dependency but doesn't include it (either directly or indirectly). This still doesn't catch indirect includes, where one header includes another header, which includes a dependency needed by the first header.

« Last Edit: September 18, 2017, 12:30:43 AM by Hooman »

Offline lordpalandus

  • Banned
  • Hero Member
  • *****
  • Posts: 825
Re: Catching all required include directories in C++
« Reply #2 on: September 18, 2017, 12:39:13 AM »
Is there any way to access the source for the header file, copy all of its code, and put that in a separate header file and thus ensure that you always have the code on hand when needing to compile.

Or, if there is specific code you only need from a specific header file, how about you copy that, put it into one of your header files and remove the original header file.

Or is headers like string pre-compiled and thus inaccessible?
Currently working on Cataclysm of Chaos, Remade.
Link to OPU page = http://forum.outpost2.net/index.php/topic,6073.0.html

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4954
Re: Catching all required include directories in C++
« Reply #3 on: September 18, 2017, 12:53:21 AM »
Not too sure what you're saying. Let me illustrate my point with an example:
Quote
$ cat incomplete.h
//#include <string>

extern std::string s;

$ g++ incomplete.h
incomplete.h:3:8: error: ‘string’ does not name a type
 extern string s;
        ^

Here I've passed the .h to the compiler. Normally you never do this. It caught that it doesn't know what "string" is. If another file had included <string>, and then included incomplete.h, there would be no error.

Quote
$ cat complete.h
#include <string>
#include "incomplete.h"

$ g++ complete.h

In this case, the compiler succeeds, and there is no output to the console.

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1014
Re: Catching all required include directories in C++
« Reply #4 on: September 21, 2017, 05:55:44 AM »
Hooman,

Thanks for the info. I'll keep trying to nail down all the includes and namespaces used by each .h/.cpp combo as a best practice. Once the module system is out of experimental I'll probably jump on that very quickly. I try to stay away from experimental features when possible (I know we are using the experimental filesystem header!).

lordpalandus,

I actually want to use the include directories as they are designed, just sort of asking what the best practice is.

-Brett