Click here to Skip to main content
15,391,277 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
The problem was I cannot properly separate the declaration & definition of a templated symbols such as classes, structs, functions, and many more. Before we could continue I have this quick statement on how I organized my work/project. We can skip this one and just go to "what I have Tried" section. thank you.

I have this kind of practices/standard way of doing c++20 module. It was divided into 3 parts;
1. Primary Module Interface unit     (*.ixx)
       - It can compose | import a list of all the module partitions available or other named-module.
       - It can contains a declaration or with a definition for your needed symbols
2. (Optional) Module Partition unit  (*.ixx)
        - same as number# 1.
3. (Optional) Module Implementation unit  (*.cxx)
       - It should only had the definition/implementation for all the needed declared symbols you had.


--------------------------------------------------------------------------------------
[Directories]
/module-name
└─src
 └─main
  └─cpp
   └─domain/name/module_name/pkg1
    └─dn-module_name-pkg1.ixx //REM: primary-module-interface inside of this pkg1
    └─ClassOne.ixx //REM: another module-interface/partition
    └─ClassOne.cxx //REM: module-implementation
--------------------------------------------------------------------------------------

1. primary-module-interface ( dn-module_name-pkg1.ixx )
C++20
export module domain.name.module_name.pkg1;
//REM: compose a list of all the module partitions available or other named-module.
export import :ClassOne;
//export import :ClassTwo;
//export import domain.name.other_module_name.pkg1
//export import domain.name.module_name.pkg2;

2. module-partition ( ClassOne.ixx )
C++20
export module domain.name.module_name.pkg1:ClassOne;
//REM: import any header unit file(s) needed. eq: import <iostream>;
//REM: import any module-named or partition. 
//REM: ep: import domain.name.module_name.pk2;
//REM: or import :OtherPartition;
//REM: or import :ClassTwo;
namespace domain::name::module_name::pkg1 {
    export class ClassOne {
    public:
        ClassOne();
        virtual ~ClassOne();
    };
}
//REM: namespace & type Aliasing
export namespace $DN$MODULE_NAME$PKG1 = domain::name::module_name::pkg1;
export using     $DN$MODULE_NAME$PKG1_ClassOne_t = typename $DN$MODULE_NAME$PKG1::ClassOne;
export typedef   $DN$MODULE_NAME$PKG1_ClassOne_t ClassOne_t;

3. module-implementation ( ClassOne.cxx )
C++20
module domain.name.module_name.pkg1:ClassOne;
namespace domain::name::module_name::pkg1 {
    ClassOne::ClassOne() { }
    ClassOne::~ClassOne() { }
}

We could add this another primary-module-interface(dn-module_name.ixx) to the upper level|directories|pkg;
--------------------------------------------------------------------------------------
[Directories]
/module-name
└─src
 └─main
  └─cpp
   └─domain/name/module_name
   └─dn-module_name.ixx //REM: primary-module-interface or my root mod-intf
   └─pkg1
    └─ ...
--------------------------------------------------------------------------------------
(dn-module_name.ixx)
C++20
export module domain.name.module_name;
export import domain.name.module_name.pkg1;
//export import domain.name.module_name.pkg2;


At the main entry point or the client side (*.cxx or .cpp or .cppm)
C++20
//import domain.name.module_name;
import domain.name.module_name.pkg1;

int main(...) {
    ClassOne_t cOne;
}


What I have tried:

For our Templated classes, struct, logic, and etc
How do We Define our Declared templated symbols/classes/etc from the module-interface/partition and then we try to Implement it to a separate file: module-implementation-unit. Such like this:

*module-partition( TemplateClassOne.ixx )
C++20
//REM: this is where we Declared our templated logic
export module domain.name.module_name.pkg1:TemplateClassOne;
namespace domain::name::module_name::pkg1 {
    export template<typename T = int>
    class TemplateClassOne {
    public:
        TemplateClassOne();
        virtual ~TemplateClassOne();
    }
}


*module-implementation( TemplateClassOne.cxx)
C++20
//REM: and this is where we Defined/Implemented our Declared templated symbols/logic
module domain.name.module_name.pkg1:TemplateClassOne;
namespace domain::name::module_name::pkg1 {
    //REM: TODO-HERE, this is not working quite well
    template<>
    //template<typename T = int>
    TemplateClassOne<>::TemplateClassOne() { }
    TemplateClassOne<>::~TemplateClassOne() { }
}

How do we do it right? On this C++20 module, Is separating it was not possible?
NOTE: the keyword "Symbols" I'm using here it means: all the data types, structs, classes, functions, identifiers and so on
Posted
Updated 27-Jan-22 22:36pm
v8
Comments
Stefan_Lang 24-Jan-22 10:07am
   
What is the question? That's a big wall of text, but no clear indication of the problem.
Phoenix Liveon 25-Jan-22 5:18am
   
@Stefan_Lang, we can find it at the "what I have Tried" portion/section. The rest of the text/statements are my way of organizing and separating a project.
Stefan_Lang 25-Jan-22 7:51am
   
You should really post your question at the very beginning. The "What have I tried" section is the very last place anyone will go looking for it!

Moreover, most questions can be answered very quickly, if we can know what information to watch out for when reading the details. Not knowing what to look for requires reading the entire text, and that does cost a lot of time.

And every single person willing to help you out will have to go through all that! Why?? You are just one person, and it takes you a lot less time to provide the single most bit of information in your posting, than it takes one hundred people to scan your entire text for it!

This section is called "Quick" for a reason! Please help posting questions in a manner that can be answered quickly.

Quote:
How do we do it right

1. Post your actual question at the top, maybe even in the topic heading
2. Provide your goal and relevant detail information after that, in the body of your posting. Make sure to keep it reasonably short by providing only the details that relate to your problem.
3. In the "What did I try" section, describe what you did to solve the problem, and the (wrong) results you got. Don't forget to describe what you expect or wish to get.
4. Make sure the formatting isn't messed up, specifically if you post code. But don't overdo it! Using large font size for most of your text will simply turn potential readers away, because it forces them to scroll more or resize the page to be able to read it properly! Just use the standards!
   
Quote:
The problem was I cannot properly separate the declaration & definition of a templated symbols such as classes, structs, functions, and many more
Short answer: you cannot! From the link I posted above (emphasis mine):
Quote:
However, when using multi-file modules, only the module interface unit contributes to the pre-compiled module. All compile-time construct therefore need to be placed in the interface file.
This shouldn't come as a surprise. Templated classes and functions require to know the types passed as template arguments before the compiler can process them. Therefore the implementation must be part of the module interface!

Looking around, these kind of questions have been asked before. Here is a more extensive answer, but it amounts to the same thing:
c++20 - How are templates handled in C++ module system? - Stack Overflow[^]


The only thing you can do is put template function declarations into one and implementations into another file, to separate them physically. But you must still include both in the module interface. Therefore there is really no advantage in doing that! The only valid reason to separate a template declaration at all is when you need forward declarations.


My advice: don't do it! The main advantage and purpose of modules is speeding up build times for stable parts of a solution. This, for the most part, are system libraries more than anything else. Creating modules for your own solution rarely offers a notable benefit, unless we're talking of large scale solutions with dozens of people working on it for an extended period of time (I'm talking years here, not months or weeks).

Most of the speed advantage you can also gain without modules, by using precompiled headers, or by splitting your projects into multiple libraries where possible.


Modules do offer some other, more formal advantages, but it seems to me that you're introducing more work than all of this is worth! Just structure your projects and headers reaonsably, and use precompiled headers.


On the other hand, if you really are dealing with a huge project, and desperately trying to speed up processes, then you should consider wrapping your template functions and classes into non-template classes and functions which internally instantiate the templates with concrete types. E. g. if your projects use three different instantiations for specific template classes, create three non-template wrapper classes for these instantiations. Considering the redundancy you will introduce, and the additional work, consider this way a last resort. Also, you don't need modules for this: you could just put these specific instantiations into precompiled headers instead, or into a separate library that only publishes the non-templated wrappers.
   
I have read, but didn't understand a word of, your question. In the topic header you mention templates, so the only advice I can offer is check out this guide:
https://itnext.io/c-20-modules-complete-guide-ae741ddbae3d[^]
The section
Quote:
Compile-time constructs vs Modules
explains that you can, indeed, use templates and other compile-time constructs in your module partitions, and even offers an example. Maybe that will help you, whatever your problem is.
   

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900