'C++20 Modules “cyclic dependency”

Im trying to include a module within another module, but i can't compile due to the following error:

"Cannot build the following source files because there is a cyclic dependency between them: Module1.ixx depends on Module2.ixx depends on Module1.ixx."

I want to modClass1_ contain modClass2_ class and modClass2_ contain a pointer to static modClass1_.


Code i tried with success with C++17 Header and Source files (.h and .cpp)

// Class1.h
#pragma once
#include "Class2.h"
class modClass2_;
class modClass1_
{
public:
    modClass1_() {};
    ~modClass1_() {};
    int V = 2;
    int getV() { return V; };
    static modClass2_ mc2;
};
extern modClass1_ mc1;
// Class1.cpp
#include "Class1.h"
modClass1_ mc1;
modClass2_ modClass1_::mc2;
// Class2.h
#pragma once
#include "Class1.h"
class modClass2_
{
public:
    modClass2_() {};
    ~modClass2_() {};
    int V = 1;
    int getV() { return V; };
    int getClass1V();
};
// Class2.cpp
#include "Class2.h"
int modClass2_::getClass1V()
{
    return mc1.V;
}
// Main.cpp
#include "Class1.h"
#include <iostream>
int main()
{
    std::cout << mc1.getV() << "\n"; // gets modClass1_ V directly
    std::cout << mc1.mc2.getClass1V() << "\n"; // gets modClass1_ V through modClass2_ through modClass1_
    std::cout << mc1.mc2.getV() << "\n"; // gets modClass2_ V through modClass1_
}

Code i tried but failed with C++20 Modules (.ixx)

// Module1.ixx
export module Module1;
import Module2;
export class modClass1_
{
public:
    modClass1_() {};
    ~modClass1_() {};
    int getV() { return V; };
    modClass2_ mc2;
    int getModule2V() { return mc2.V; };
    int V = 1;
};
export modClass1_ mc1;
// Module2.ixx
export module Module2;
import Module1;
export class modClass2_
{
public:
    modClass2_() {};
    ~modClass2_() {};
    int getV() { return V; };
    int getModule1V() { return mc1.V; };
    int V = 2;
};

Any help/suggestion will be appreciated.

Environment: Visual Studio 2019 | MSVC-2019 | C++20 | Windows 10 Pro



Solution 1:[1]

Just like with header files, you can separate out module interface files from module implementation files. Example:

Module1.ixx:

export module Module1;

import Module2;

export class modClass1_
{
public:
  modClass1_() {};
  ~modClass1_() {};
  int getV() { return V; };
  modClass2_ mc2;
  int getModule2V() { return mc2.V; };
  int V = 1;
};
export modClass1_ mc1;

Module2.ixx:

export module Module2;

export class modClass2_
{
public:
  modClass2_() {};
  ~modClass2_() {};
  int getV() { return V; };
  int getModule1V();
  int V = 2;
};

Module2.cpp:

import Module1;
import Module2;

int modClass2_::getModule1V()
{
  return mc1.V;
}

main.cpp:

#include <iostream>

import Module1;
import Module2;

int main()
{
  // NB: mc1 is a symbol imported from Module1
  std::cout << "mc1.V: " << mc1.V << '\n';
  std::cout << "mc1.getModule2V: " << mc1.getModule2V() << '\n';

  modClass2_ mc2;
  std::cout << "mc2.V: " << mc2.V << '\n';
  std::cout << "mc2.getModule1V: " << mc2.getModule1V() << '\n';
}

Note that modClass2_'s interface doesn't require anything from Module1 and therefore Module2.ixx doesn't have import Module1;. Module2.cpp, where the implementation lives, does.

In my example I've moved as little as possible from Module2.ixx into a Module2.cpp implementation file but in practice you might well want to move more things out of the interface.

Solution 2:[2]

I had a tree data structure split into two modules that needed to reference each other, and posted an answer how to make it work. Copypasting it, here is a desperate solution to break a circular dependency by using a template:

// A_impl.cc

export module A_impl;

export template <typename B> class A_impl {
    public:
        void f(B& b) {}
};
// B.cc

export module B;

import A_impl;

export class B;

typedef A_impl<B> A;

export class B {
    public:
        void f(A& a) {}
};
// A.cc

export module A;

export import A_impl;
import B;

export typedef A_impl<B> A;
// main.cc

import A;
import B;

int main(void) {
    A a;
    B b;

    a.f(b);
    b.f(a);

    return 0;
}

At the moment clang doesn't support module partitions so with that toolchain this seems to be the only way to define A and B in different files (without #include) while placing them in modules. With Visual Studio module partitions may or may not allow a cleaner structure.

Solution 3:[3]

Tested with gcc, module partitions can solve the problem using forward declarations and internal module linkage. Note: This doesn't setup modules to depend on each other, the entire cyclic dependency is defined in a single module.

// A.cc

export module Cyclic:A;

export class B;
export class A {
public:
    char name() { return 'A'; }
    void f(B& b);
};
// B.cc

export module Cyclic:B;

export class A;
export class B {
public:
    char name() { return 'B'; }
    void f(A& a);
};
// A_impl.cc

module Cyclic:A_impl;

import Cyclic:A;
import Cyclic:B;

import <iostream>;

void A::f(B& b) {
  std::cout << name() << " calling " << b.name() << std::endl;
}
// B_impl.cc

module Cyclic:B_impl;

import Cyclic:B;
import Cyclic:A;

import <iostream>;

void B::f(A& a) {
  std::cout << name() << " calling " << a.name() << std::endl;
}
// Cyclic.cc

export module Cyclic;
export import :A;
export import :B;

Solution 4:[4]

In Visual Studio Code you can select the Azure Subscription to which deploy your functions by opening the Azure tab on the left side menu (or Alt + Shift + A shortcut) and by clicking the "Select subscription" button under the "Functions" tab of that menu (see screenshot below).

screenshot

Solution 5:[5]

If your account has multiple subscriptions, then click this icon to select the other subscription where you need to deploy your function app or any other app as you can see in the below image:

enter image description here

even though I've signed out with my personal account and logged into the customer-provided account.

If the customer's subscription is available in your account, though you logged out from the account, it may have shown your subscriptions at the azure extension.

  • Press Ctrl + Shift + P in your VS Code, which opens the command palette.

  • Click on Azure: Sign Out for complete sign out in VS Code. enter image description here

  • If you signed in again to the VS Code through Command Palette or Account extension on the sidebar, sync will not happen automatically. So, Click on Sign in to Sync settings which logs you to your Microsoft account to verify again.

  • Make sure this setting is turned on whichever the account (Customer's or Your Subscription) you logged in to VS Code. enter image description here

This setting helps you to deploy your applications with any subscription in your account through the VS Code Editor.

Solution 6:[6]

Although other responses were useful to me and might prove useful to others, my specific problem was caused by the "Azure: Tenant" setting in the Visual Studio Code Settings. This setting basically sets a specific subscription to use (identified by its ID). Once removed, with this field empty, the default behavior of the extension is to ask each time where to deploy a function. As soon as I removed the value for this setting the second subscription appeared in Azure extension panel.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nathan Pierson
Solution 2 jjrv
Solution 3
Solution 4 Telemaco019
Solution 5 SaiSakethGuduru-MT
Solution 6 Francesco Manfredi