Compiling header files or freebie documentation
Who is this article for?
It is unlikely that experienced C ++ developers will find anything new and interesting in this article. Maximum banal team
gcc -c -x c++ -I ./ */*/*/*/*.h
that they already know.
But if you are a beginner developer, or just build documentation for your project for the first time, or try it once, but when you see the nonsense that doxygen generated, delete it and forget it like a bad dream, welcome to the cat, most likely you will find then a couple of useful thoughts.
C ++ programming is primarily an analysis of existing code.
For several years now, I have been participating in the development of a simulation system. Development is carried out on the great and powerful ... brainchild of Straustrup. And, I must say, this project has long been written not so much in C ++ as in its own dialect from macros (below it will become clear why this is important). I think this situation is familiar to many C ++ developers.
When I was just starting to study this project, they immediately advised me to build documentation on it using doxygen. But for some reason, friendship with this tool did not work out, and the next time I remembered about it already years later.
Time passed, my professionalism (as I hope) grew, the project developed and I wanted to restore order in the library, which was actively ruled. By order here is meant the concept of one header file = one entity (or several small and closely related ones), as well as the separation of all files into three types - header, internal and source - respectively * .h, * .inl, * .cpp. Moreover, the separation should be carried out in such a way that in the header files there is no definition of a single member function of the class, and all of them were either in cpp files or in inl files. In addition, all the module code should have been formatted according to a single standard, and, most interestingly, most entities should have been commented out using special doxygen commands (\ interface, \ class, \ todo, \ brief, \ enum and others) .
No sooner said than done. After some weeks of killed evenings, the insane task has been completed! The code is beautiful so that a little tear comes.
And the time has come to sit back in the chair while waiting for doxygen to build the most beautiful and correct documentation for me with a description of my (at that time already beloved) module of the system. And with a sinking heart However, what appeared to my eyes was, to put it mildly, garbage. Doxygen frankly cheated: the diagrams are incomplete, namespace'empty, macros trying to give me functions, in general, can’t count everything ... But the initial analysis showed that he did not understand the macros (all namespace'y, smart pointers (own production, by the way) and much another was set using macros).
Solution in option PREDEFINED?
First of all, doxygen settings were suspected. Options such as ENABLE_PREPROCESSING, MACRO_EXPANSION, EXPAND_ONLY_PREDEF, SEARCH_INCLUDES and INCLUDE_PATH have been double-checked. But the settings looked logical, and the macros did not begin to be perceived correctly. Then the PREDEFINED option came into play, although it did not answer the question “why does oxygen mess up?”, But it allowed him to explain the necessary macros. This was the solution to the problem for a while. But the fact that it was necessary to constantly add all the new macros to this option was very depressing and made us continue to study doxygen.
The problem is in the code!
I thought for a long time about the behavior of doxygen and google it. And once he even leaked it to himself on svn with the righteous thought of finding and fixing a terrible bug that prevented him from processing macros :)
But in time I stopped, for I understood the difference between it and the compiler. They have very different goals (and Doxigen is even more complicated in some way), because he needs to not only understand the cpp files, but also do the same with the header files, so that he can show it all to the user beautifully, i.e. by file. And here comes something interesting that I did not think about before: the compiler is not interested in header files, it receives a “translation unit”, or the output of a preprocessor with already worked out #include directives. I allow myself to rephrase this in this way - the compiler deals with header files only indirectly, so to speak, in the context of a translation unit. And compiles it in this very context. This leads to a disappointing conclusion - the header file may be incorrect by itself,
But with doxygen, this does not work - the header file is analyzed as a stand-alone, self-sufficient document with source code. And if it lacks declarations of used entities (which appear in the context of using this header file), then doxygen will be mistaken. And it is very likely that it was this disease that befell our project.
So what error, elusive for the compiler, is hidden in the header file? This is not the #include directive for those files where the notorious macros have been defined. Those. when compiling cpp-files, all definitions fell into the current translation unit in some roundabout way, and not through the “problem” header file. After this discovery, a meeting was held in our team, with the main issue on the agenda “and what to do with it, actually?” The question was resolved in favor of the fact that such behavior - the absence of all the necessary inclusions - is a mistake. The main argument is quite simple - the header file could potentially be included anywhere, and therefore it should be so self-sufficient that it compiles.
The solution is “compilation of header files”
This is where the question of “compiling header files” came up.
The point of this event is to make the compiler analyze all the header files without adding them to the context of the source (* .cpp) files, and report errors. And then, if they are fixed, then doxygen should not have any excuses to properly build project documentation with all the diagrams and more.
Now it's time to talk directly about the compiler used in our team.
For historical reasons, this is ms visual studio, the standard as much as 2008. But literally before the end of this story, the project of translating the computing core of the system under GNU / Linux was successfully completed. And naturally, in this environment, I was selected as the GCC compiler, version 4.6.
And I began to torment him with requests, they say compile me the headlines. And he resisted for a long time ... until he caved in.
A thoughtful reading of his man showed me the -I option, with which GCC you can specify the path from which to search for inclusions in quotation marks.
Here, one important fact should be noted, in our project we have long (and very successfully) adhered to the rule "indicate the path to the file in the inclusion from the root of the project." This discipline made it possible to dispense with the single -I option.
Then it remains to submit to the gcc input all the project header files. But here there was some hitch due to the reluctance of gcc to accept the output of the ls command through the pipeline as an input. But the solution turned out to be even more trivial, gcc already searched for input files by the transferred mask.
So the team
g++ -I ./ */*/*/*/*.h */*/*/*.h */*/*.h
fully validates all library header files.
The next time gcc began to resist, if it was given * .inl files as input. The options -c (only compilation without linking), and -x c ++ (explicitly setting the programming language) helped.
The final command used to validate the header (* .inl files we decided to leave alone) files:
gcc -c -x c++ -I ./ */*/*/*/*.h */*/*/*.h */*/*.h
Well, then, it remains to fix the errors and use doxygen.
And, in general, for sure, you can improve something, for example, screw cmake here, but the main idea of this post is the need to “compile header files” to combat the seemingly strange behavior of doxygen. So I will stop here until I tire of the last most assiduous reader;)
Instead of conclusions
And actually, why the post is called "... or documentation for free."
Yes, it’s just that the code may not have a single line of doxygen’s comment, but you can still build documentation on it with all kinds of diagrams, which can be very helpful when studying a new project. Namely, the automatic construction of documentation (with convenient navigation and diagrams) was supposed to make it easier for new students (and all this happens in a technical university) to study a very large and complex system and was the original purpose of using doxygen in our project.
Naturally, in the comments I look forward to constructive criticism, corrections, additions of my moderately crooked commands and questions.