Поделиться через


Stop writing header files

Although one of my favorite languages, C++ has a number of issues that make it difficult to write and maintain. One of the worst is its compilation model: you import declarations using fragile literal text inclusion (#include), and you have to waste your time keeping these declarations redundantly in sync with the source files. Templated function definitions require even more redundancy. The same information is really in both places - this violates The Pragmatic Programmer 's oft-repeated tip 11, DRY (Don't Repeat Yourself).

Worse yet, the line between what goes in the header file and source file looks like it was drawn by a drunkard. You have an inline function? Put it in the header file. You want to make it not inline anymore? Put it in the source file. You want to template it? Back in the header file it goes. In C there are additional issues, since a function definition that is out of sync with its prototype results not in a compile-time error but an implicit declaration and a mysterious link error.

So what's the solution? The best solution is to simply stop writing header files. Instead, write the source file, mark the nonredundant information that needs to go into the header, and then use a build tool to generate your header based on this information prior to compilation. The header can never be out of sync, you can make changes more quickly, you drop the number of lines of code you have to actively maintain, and everybody is happier.

Of course, that sounds like a lot of work - especially the part that involves writing a tool that has to parse a nontrivial subset of C++. Luckily, some fellow victims out there have already produced tools that do this. The best one I've come across so far is Lzz: The Lazy C++ Programmer's Tool, a tool that allows you to lay out function definitions in C++ code in much the same way as Java or C# (within the class), and then it produces both the header and source files. Here's a sample:

 class A {
  public:
    inline void f (int i) {
        if (i <= 0)
            g();
        else
            g(i);
    }

  private:
    void g (int j = 0) {
        if (j > 0)
            g(j - 1);
    }
};

The function f ends up in the header file and g in the source file. If I remove the "inline" keyword, however, f gets moved to the source file. Don't have to worry about it.

Lzz has some limitations though, so make sure you read the documentation.  It also includes a couple interesting extensions called Lazy classes and Lazy functors that I won't get into here. Try it out and see if you're willing to kiss your header files goodbye.

Derrick Coetzee
This posting is provided "AS IS" with no warranties, and confers no rights.

P.S. For those of you waiting on the Bloom filter code, I've got something written and I'm just waiting for approval from the powers that be. Hang in there.

Comments

  • Anonymous
    August 19, 2005
    I guess it could be argued that the header is an artifact from the age of static linking when code shipped in an object or library file and the header was needed for the program to find the correct entry points into the object or library file.

    With .NET all of this information is now exposed as metadata and with COM, it is exposed as either IDL or typelibraries. With .NET the header is completely redundant and with COM, there are other files with the same information.

    C++ was designed to be a powerful low-level compiler. It also is one of the most terse languages. Extraneous information is almost entirely against the spirit of the language.

    Perhaps someone in the Visual C++ team could add a option to fix this or someone could write a powertoy for VS 2005. This effects productivity. Power is the most important feature of C++ but productivity is also important.

  • Anonymous
    August 21, 2005
    I think "tools" like this are an OK idea if you're working on a small project - something involving one or two developers, and who are the only people who will ever maintain it. But the kind of things that I have to take hold of - 5 years old, spaghetti code, people who wrote it have long since left, but absolutely critical to the business - it would be a nightmare. LZZ is essentially a new language, on which there are no books, very little documentation (relative to C++) and only supports as subset of C++.

    There are plenty of IDEs and plugins that can generate declaration and definition stubs and refactoring tools that can move functions between source and header files.

    Just my 2p's worth :)

  • Anonymous
    August 21, 2005
    Thanks for your comments, guys.

    Although headers were used to describe library interfaces, really this was just because they were there already. Although I'm not certain, I believe the convention of the header (and it is just a convention) evolved from C's antiquated requirement that prototyped functions be declared before they're used, and the need to do so in a way that doesn't create too much redundancy (like the older practice of listing imported definitions in every source file). I agree that an integrated tool or compiler option in Visual Studio to make headers go away would be quite handy for new code.

    I also agree that it's more difficult to dispose of headers in existing code, but there are more incremental approaches where individual headers could be phased out over time. The solutions Mike suggests help initially, but don't significantly reduce maintenance burden, in my opinion. The Pragmatic Programmer spoke out against one-time generated code strongly, such as that generated by code wizards, due to the added maintenance effort they create, and automatic code motion doesn't counteract the readability problems created by placing related methods in different files. Also, although LZZ may not be appropriate for your task, I should clarify that most of the code is copied rather than parsed. Much better though would be a tool that can easily migrate old projects to a less redundant syntax with little to no human intervention. I'm also not sure what the negative impact on Intellisense and the debugging experience might be, but these are important to consider also.

  • Anonymous
    January 11, 2008
    Have you come across any other tools that do this?  The lzz one seems kind of dead, as there hasn't been a new release since late 2003.

  • Anonymous
    May 28, 2009
    PingBack from http://paidsurveyshub.info/story.php?title=developing-for-developers-stop-writing-header-files