Home > database >  How to use C module :private fragment with templates?
How to use C module :private fragment with templates?

Time:12-22

I'm experimenting with C 20 to better understand how they work in practice. I learned about the module :private fragment that can be used to separate the interface from the implementation, while keeping both in the same file. That seems to work for standard functions, but not for template functions.

I have the following files:

// File "main.cpp"

#include <iostream>

import mymodule;

int main()
{
    std::cout << "greeting(): " << mymodule::greetings() << std::endl;
    int x = 1;
    int y = 2;
    std::cout << "add(x, y): " << mymodule::add(x, y) << std::endl;
}
// File "mymodule.ixx"

module;

#include <string>

// declare interface
export module mymodule;

export namespace mymodule {
    template<typename T>
    T add(T x, T y);

    std::string greetings();
}

// implement interface
module :private;

std::string mymodule::greetings() {
    return "hello";
}

template<typename T>
T mymodule::add(T x, T y) {
    return x   y;
}

And get a compiler warning and a linker error (using Visual Studio 2022, MSVC):

Rebuild started...
1>------ Rebuild All started: Project: PlayingWithModules, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>mymodule.ixx
1>Compiling...
1>mymodule.ixx
1>C:\Users\Sam\Development\Cpp\Sandbox\PlayingWithModules\mymodule.ixx(29,13): warning C5226: 'mymodule::add': exported template defined in private module fragment has no reachable instantiation
1>main.cpp
1>main.obj : error LNK2019: unresolved external symbol "int __cdecl mymodule::add<int>(int,int)" (??$add@H@mymodule@@YAHHH@Z::<!mymodule>) referenced in function main
1>C:\Users\Sam\Development\Cpp\Sandbox\x64\Debug\PlayingWithModules.exe : fatal error LNK1120: 1 unresolved externals
1>Done building project "PlayingWithModules.vcxproj" -- FAILED.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

My understanding is that mymodule::greetings() is fine, but mymodule::add(x, y) is not because the function call mymodule::<int, int>(x, y) cannot be seen by the compiler, which results in no function <int, int> being generated.


If I instead implement the template function as part of the interface:

module;

#include <string>

// declare interface
export module mymodule;

export namespace mymodule {
    template<typename T>
    T add(T x, T y) {
        return x   y;
    }

    std::string greetings();
}

// implement interface
module :private;

std::string mymodule::greetings() {
    return "hello";
}

Then everything compiles and works as expected.


Is it possible to use module :private with function template, and if yes how? Or should template functions always be implemented as part of the interface? I cannot find details online regarding templates usage in the context of modules, and cannot find references to the compiler warning I get.

CodePudding user response:

Modules do not change the nature of C , merely how you access different components.

It is part of the nature of C that, in order for a translation unit to use a template, that translation unit must have access to the definition of that template. Modules doesn't change that.

The private module fragment specifically contains things that are not part of the interface of the module. That's the whole point of them: to be able to stick something that isn't part of the interface into a module file.

A private module fragment therefore can only contain the stuff that you would put into a traditional .cpp file. Indeed, that's why they exist: so that (for short modules) you can put all the usual .cpp stuff in a single file without affecting what gets generated by the module.

CodePudding user response:

Private module fragments seems like an undocumented feature, intended for future extension for the language, as per cppreference. Also note that modules are not fully implemented, for any compiler, so you will find things that will work with clang but break with msvc, etc.

But if I understand your question correctly, you really care about having stuff in one file, you can do exactly that by not having any distinction between declaration and definition:

// File "mymodule.ixx"

module;

#include <string>

// declare interface
export module mymodule;

export namespace mymodule
{
    template<typename T>
    T add(T x, T y) {
        return x   y;
    }

    std::string greetings() {
        return "hello";
    }
}

Note

Modules are paving the road for a new C -era. What I mean by this is that we avoid the distinction between declaration in header files and definition in source files, because we will not need to have any header files.

This will simplify the language greatly.

Since private module fragments seems like an undocumented feature, I will suggest one of three options:

  1. Do as above (ie. declaration == definition)
  2. use a module implementation unit
  3. use a private module fragment

I have answered how to accomplish 2) and 3) here.

Since I don't have access to an msvc-compiler set up, I cannot check how to fix your example right away. Note, module implementation is largely unfinished business by compiler vendors.

  • Related