It looks like I found a bug in Visual Studio how it processes module code.
Here's some non-module code that compiles and runs fine, a couple of class header files and an test implementation source file:
Box.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
#ifndef BOX_HPP
#define BOX_HPP
#include <iostream>
class Box
{
public:
Box(double l, double w, double h) : m_length { l }, m_width { w }, m_height { h }
{ std::cout << "Box(double, double, double) called.\n"; }
explicit Box(double side) : Box { side, side, side }
{ std::cout << "Box(double) called.\n"; }
Box() { std::cout << "Box() called.\n"; }
double volume() const { return m_length * m_width * m_height; }
double getLength() const { return m_length; }
double getWidth() const { return m_width; }
double getHeight() const { return m_height; }
private:
double m_length { 1.0 };
double m_width { 1.0 };
double m_height { 1.0 };
};
#endif
|
Carton.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
#ifndef CARTON_HPP
#define CARTON_HPP
#include <string>
#include <string_view>
#include "Box.hpp"
class Carton : public Box
{
using Box::Box; // inherit Box class constructors
public:
Carton(double length, double width, double height, std::string_view mat)
: Box { length, width, height }, m_material { mat }
{ std::cout << "Carton(double,double,double,string_view) called.\n"; }
private:
std::string m_material { "Cardboard" };
};
#endif
|
main_no_modules.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
#include <iostream>
#include "Carton.hpp"
int main()
{
Carton cart; // calls inherited default constructor
std::cout << '\n';
Carton cube { 4.0 }; // calls inherited constructor
std::cout << '\n';
Carton copy { cube }; // calls default copy constructor
Carton carton { 1.0, 2.0, 3.0 }; // calls inherited constructor
std::cout << '\n';
Carton cerealCarton(50.0, 30.0, 20.0, "Chipboard"); // calls Carton class constructor
}
|
While inheriting base class constructor(s) in a derived class is something I haven't seen before I understand what it is doing.
This code compiles and has the following output:
Box() called.
Box(double, double, double) called.
Box(double) called.
Box(double, double, double) called.
Box(double, double, double) called.
Carton(double,double,double,string_view) called. |
"Modularizing" this code isn't all that hard. Remove the header guards in the headers, change the file extension from hpp to cppm, add export
name; add the export keyword to the class definitions and change the #includes to import statements. Change the #includes in the test implementation file to imports.
Box.cppm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
export module box;
import <iostream>;
export class Box
{
public:
Box(double l, double w, double h) : m_length { l }, m_width { w }, m_height { h }
{ std::cout << "Box(double, double, double) called.\n"; }
explicit Box(double side) : Box { side, side, side }
{ std::cout << "Box(double) called.\n"; }
Box() { std::cout << "Box() called.\n"; }
double volume() const { return m_length * m_width * m_height; }
double getLength() const { return m_length; }
double getWidth() const { return m_width; }
double getHeight() const { return m_height; }
private:
double m_length { 1.0 };
double m_width { 1.0 };
double m_height { 1.0 };
};
|
Carton.cppm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
export module carton;
import <string>;
import <string_view>;
import <iostream>;
import box;
export class Carton : public Box
{
using Box::Box; // inherit box class constructors
public:
Carton(double length, double width, double height, std::string_view mat)
: Box { length, width, height }, m_material { mat }
{ std::cout << "Carton(double,double,double,string_view) called.\n"; }
private:
std::string m_material { "Cardboard" };
};
|
main_modules.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
import <iostream>;
import carton;
int main()
{
Carton cart; // calls inherited default constructor
std::cout << '\n';
Carton cube { 4.0 }; // calls inherited constructor
std::cout << '\n';
Carton copy { cube }; // calls default copy constructor
Carton carton { 1.0, 2.0, 3.0 }; // calls inherited constructor
std::cout << '\n';
Carton cerealCarton(50.0, 30.0, 20.0, "Chipboard"); // calls Carton class constructor
}
|
So far nothing really difficult to understand, converting existing non-module code to modules is not that hard. And I haven't had any problem with other module code I've mucked around with until now.
Compile the above and VS vomits up an error:
Error C2512 'Carton': no appropriate default constructor available |
Comment out lines 7 & 8 and the error goes away.
Huh? Somehow non-modular code that compiles fine gives VS heartburn when done as modules. Is this a bug in VS or a possible bug in the over-all C++ implementation of modules?
I wish I could test this in a different compiler, but so far MSVC is the only compiler I know of that has module support. All the others have partial to no support for modules. ::sad::