24

I'm used to writing code without modules where the header files contain the function declarations like:

// foo.h 
class Foo
{
    void bar();
};

and the corresponding .cpp file contains the definition:

// foo.cpp
#include "foo.h"

void Foo::bar()
{
    // ...
}

To my knowledge, this is done to decrease compile time and reduce dependencies. When modules will be used, will this still apply? Would it be just as fast to have the class in a single file with the definitions the way Java and C# does it? If this is the case, will there be any need for both .hpp and .cpp files when using modules?

Community
  • 1
  • 1
Phlox Midas
  • 4,093
  • 4
  • 35
  • 56
  • How will you reference a function? If you #include your combined header and .cpp file you will be multiply defining functions. – Ant Apr 28 '17 at 13:47
  • I had the same thought. I always found that separating declarations and impl in C++ is more trouble than it's worth. And this is mostly because of templates ... this is why many modern generic C++ libraries are header only. I see modules as a great leap forward for C++; finally separating it from its C heritage. – rmccabe3701 Jan 15 '22 at 02:44

4 Answers4

11

The only reason I'm aware of, as the modules proposal currently stands, is to handle circular interface dependencies.

If a program is made up of modules and it does not separate function declarations from definitions, all module files will be module interfaces (as opposed to module implementations). If you want to compare them to header and code files, module interfaces could be seen as the header (.hpp) file, and module implementations could be seen as the code (.cpp) files.

Unfortunately, the modules proposal does not allow cyclic module interface dependencies. And since your program is now completely made up of module interfaces, you will therefore never be able to have two modules which depend on each other in any way (this may be improved by the proclaimed ownership declaration in the future, but this is currently not supported). The only way to resolve circular module interface dependencies is by separating declarations and definitions and placing the circular imports in the module implementation files, as opposed to circular module interface dependencies, circular module implementation dependencies are allowed.

The following code provides an example of a situation that is impossible to compile without separating declarations and definitions:

Foo module file

export module Foo;

import module Bar;

export namespace Test {
    class Foo {
    public:
        Bar createBar() {
            return Bar();
        }
    };
}

Bar module file

export module Bar;

import module Foo;

export namespace Test {
    class Bar {
    public:
        Foo createFoo() {
            return Foo();
        }
    };
}

This article shows an example on how this could be solved were the proclaimed ownership declaration available. In essence, it comes down to separating declarations and definitions.

In a perfect world the compiler would be able to handle this scenario, but alas, as far as I'm aware the current proposed implementation of modules does not support it.

Qub1
  • 1,154
  • 2
  • 14
  • 31
  • 3
    I never saw circular dependencies as something desirable. Rather, I considered it a design problem. In this sense, is it even a good idea to support this? – BitTickler Jul 19 '18 at 21:47
  • 4
    @BitTickler I do agree with you that circular dependencies can indicate a design issue, but this is not always the case - and sometimes it is just difficult to avoid circular dependencies. Consider the scenario where you have a tree of nodes, where each node would like to reference its parent and children. This creates a circular reference. This is true for pretty much any parent <-> child relationship, such as for example a Player and Item class, where the Player class refers the items it has and the Item class refers its owner. – Qub1 Jul 20 '18 at 12:37
3

There are still lots of reasons to use header files.

The ease of sharing and understanding an object api without seeing the underlying details is of enough use to keep them around. It's a good 20 ft view of an object, essentially being an outline.

If you are selling a library, you would include a header file, as well as an archive file or a shared library. This way you can keep information proprietary without compromising the IP of your product, and your customers can include a binary compiled for their target.

I don't believe this is possible without header files.

Ethan
  • 59
  • 4
2

There is a nice discussion here that explains the idea of modules.

In short you are right, the separation between header files and implementation files will no longer be needed. The #include directive will be replaced by the import directive, and at compile time the module will provide the required information that would otherwise be in the included header file.

Gert Wollny
  • 529
  • 2
  • 8
1

Another use of this idiom is inherited from C; it is a convenient means of forward-declaration even for translation units that have no dependencies on other translation units.

The use of precompiled headers didn't really become an important thing until C++'s expanded use of header files made it necessary for performance reasons (notwithstanding some very large old school headers like windows.h).

The idea does seem to be to bring something more like the C#/Java mechanism into play. The C++ mechanism is very Modula/ADA in spirit. It would be nice to have the machines do more of the work for us.

Kyle Huff
  • 169
  • 7