2

I have a large class Foo that I want to turn into a module partition. This class is only used internally, so I don't need to export any symbol. A module implementation partition should do:

export module parent;
import :foo;

with

module parent:foo;
class Foo { ... }; urg! I need to add the full implementation here, no more hpp/cpp split

I could hack my way around the issue by having a module interface partition parent:foo that would contain my header file but wouldn't export anything. I am not a big fan of the idea though.

What is the recommended approach here? Should the "old-fashioned" hpp/cpp split die with modules, when nothing is to be exported?

0

3 Answers 3

4

You can still use separate source files in much the same fashion:

module parent:foo;
class Foo {
  void f();
};
module parent;
import :foo;

void Foo::f() {}

The latter implementation unit, being only definitions of functions declared elsewhere, need not be imported anywhere, just linked as usual. The same approach applies to exported classes except that they must of course be declared in an interface unit.

Whether you want this structure for the future is a separate question: modules may make your build fast enough even without the duplicative isolation, and your implementation may, as a feature, decline to inline functions not declared inline in a module unit (even if defined inside a class) to provide ABI stability.

Sign up to request clarification or add additional context in comments.

7 Comments

not sure I follow. "need not be imported anywhere"? You still need import :foo in the parent module, no? in your example, you have the definition in the parent module. Do you mean you could just as well have multiple implementation partitions, one containing the declarations (~hpp) and another one containing the definitions (~cpp)?
@Touloudou: You need import :foo; in whatever part of the module uses the implementation partition, but that imports only the first of these two translation units. The latter is not a partition in any formal sense, even if we think of it as the implementation of parent:foo.
If I can only import one implementation partition, how can I overcome the issue then? The example you provided has the definitions in the parent module. In a large application, I don't want to put all my children's implementations in the parent module.
@Touloudou: We’re miscommunicating. You can import :foo; import :baz; as much as you like. You can also have as many translation units with a plain module parent; as you like (the name is misleading in this sense). Those implementation units aren’t “part of” the partition, but they don’t need to be.
@alexpanter: To the extent that implementations opt into this behavior, modules restore some of the original meaning of inline in that they will not actually inline any non-inline function in a module unit into any function defined in a different translation unit. In addition to providing ABI stability, this can improve optimization by allowing internal-linkage functions to never have any code generated for them. (The ODR issue doesn't really exist with modules since there is only one definition in total of any function attached to a named module.)
|
2

You don't need any header files when working with modules. This is the core idea of introducing modules to the language - to gradually terminate the need for header-inclusion. Your example code looks perfectly fine, especially if you want the Foo class to only be visible inside the module:

// parent.cpp
export module parent;
import :foo;

// Here we can use all functionality provided by
// the foo partition without worrying that it will
// be leaked outside this module.
// parent-foo.cpp

// All code in here will only be visible inside the
// parent.cpp file.

module parent:foo;
class Foo { /* ... */ };

Alternatively, you can also place everything in one file if you will. The compiler will not care, though that file may become large:

// parent.cpp
export module parent;

class Foo { /* ... */ } // not visible outside this module!

export
{
    // Everything placed in here will be visible to other
    // files that import this module.
}

3 Comments

Exactly. Modules provide a much easier way of doing things, but programmers get so used to doing things the hard way that they still want to separate declaration and implementation.
@DaveDoknjas I agree almost completely. But, with header distribution people may browse through source code, getting valuable insights not necessarily provide by the official documentation. By eliminating header files, do you think we need a more standardized/enforced way of providing documentation?
Why does this only seem to be a problem in the C++ world? Large systems are developed with other languages and don't require separate declaration/implementation. Any IDE used in the last 20 years allows collapsing code to just focus on interface details. I think the benefits of eliminating header files far outweigh the disadvantages.
0

If you want to keep your file structure, there's nothing stopping you from converting

foo.hpp

class Foo { ... };

foo.cpp

#include "foo.hpp"

Foo::Foo() ...

Into

foo.hpp

module parent:foo;
class Foo { ... };
#include "foo.cpp"

foo.cpp

Foo::Foo() ...

1 Comment

This is technically a working solution, but it poses problems if processing of foo.hpp depends on preprocessor configuration: "preprocessing macros defined in the translation unit will not affect the processing of the header file. This may be inconvenient in some cases (some header files uses preprocessing macros as a form of configuration), in which case the usage of global module fragment is needed." (cppreference)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.