Home > Software engineering >  Cyclic dependencies with C 20 modules and the private fragment
Cyclic dependencies with C 20 modules and the private fragment

Time:12-23

Can the private module fragment be used to break cyclic dependencies in the same way that implementation source files can? This example is using Visual Studio 2022 Preview.

This is an example that compiles successfully without the private module fragment and how I expect you would solve this issue.

ModuleA.ixx

export module ModuleA;
import ModuleB;

export class ClassA
{
public:
    ClassB foo;
    void sayHello();
};

ModuleA.cpp

module;
#include <iostream>
module ModuleA;

void ClassA::sayHello()
{
    std::cout << "Hello" << std::endl;
}

ModuleB.ixx

export module ModuleB;

export class ClassB
{
public:
    void sayHello();
};

ModuleB.cpp

module ModuleB;
import ModuleA;

void ClassB::sayHello()
{
    ClassA bar;
    bar.sayHello();
}

main.cpp

import ModuleB;

int main()
{
    ClassB baz;

    baz.sayHello();

    return 0;
}

This is an example using the same main.cpp and the private module fragment that results in a cyclic dependency error.

ModuleA.ixx

export module ModuleA;
import ModuleB;

export class ClassA
{
public:
    ClassB foo;
    void sayHello();
};

module :private;
import <iostream>;

void ClassA::sayHello()
{
    std::cout << "Hello" << std::endl;
}

ModuleB.ixx

export module ModuleB;

export class ClassB
{
public:
    void sayHello();
};

module :private;
import ModuleA;

void ClassB::sayHello()
{
    ClassA bar;
    bar.sayHello();
}

So, what is the difference between these two implementations and why does one work but the other doesn't?

CodePudding user response:

Your original example worked because each of the four files were separate translation units (TU). The implementation units each required the other modules' interface units, but the interface units themselves had no dependencies on the implementation units. So both interface units could be compiled before either implementation unit. Thus both module interfaces are available to both implementation units.

By combining each module's interface and implementation units into the same file, you put them in the same TU. The fact that part of it is in a private module fragment changes nothing about this. They are compiled at the same time since they're all part of the same TU. So the import ModuleB directive must be processed while compiling ModuleA, which means that ModuleB must be compiled, which requires processing the import ModuleA directive.

Which is a cycle.

Put simply, private module fragments are not separate files to be processed by the compiler.

  • Related