Question: What am I doing to cause a multiple definition symbol linker error?
OSFrameworkWindows10Module.ixx
module;
#include <memory>
export module OSFrameworkWindows10Module;
export class OSFramework {
private:
struct impl;
std::unique_ptr<impl> _impl;
public:
OSFramework();
~OSFramework();
};
typedef struct OSFramework::impl {
impl();
~impl();
} impl;
impl::impl()
{}
impl::~impl() = default;
OSFramework::OSFramework() : _impl{ std::make_unique<impl>() } {}
OSFramework::~OSFramework() = default;
winmain.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
import OSFrameworkWindows10Module;
int WINAPI wWinMain (
_In_ HINSTANCE hinst,
_In_opt_ HINSTANCE,
_In_ PWSTR,
_In_ int
)
{
auto os_framework = std::make_unique<OSFramework>();
return S_OK;
};
compiler output
1>------ Build started: Project: Vegas22Metroidvania1, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>OSFrameworkWindows10Module.ixx
1>Compiling...
1>OSFrameworkWindows10Module.ixx
1>winmain.cpp
1>OSFrameworkWindows10Module.ixx.obj : error LNK2005: "public: __cdecl OSFramework::~OSFramework(void)" (??1OSFramework@@QEAA@XZ::<!OSFrameworkWindows10Module>) already defined in winmain.obj
1>D:\projects\Vegas22Metroidvania1\x64\Debug\Vegas22Metroidvania1.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Done building project "Vegas22Metroidvania1.vcxproj" -- FAILED.
Pretty lost here as to why OSFramework::~OSframework() is being counted as defined more than once. I'm somehow instantiating it in my winmain.cpp file even though I'm just setting up a smart pointer for the class instance.
Maybe relevant info: I'm using Visual Studio 17.4.3
CodePudding user response:
I ended up separating implementation from interface:
winmain.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <memory>
import OSFrameworkWindows10Module;
int WINAPI wWinMain (
_In_ HINSTANCE hinst,
_In_opt_ HINSTANCE,
_In_ PWSTR,
_In_ int
)
{
auto os_framework = std::make_unique<OSFramework>();
return S_OK;
};
OSFrameworkWindows10Module.ixx
module;
#include <memory>
export module OSFrameworkWindows10Module;
export class OSFramework {
private:
struct impl;
std::unique_ptr<impl> _impl;
public:
OSFramework();
~OSFramework();
};
OSFrameworkWindows10.cpp (new)
#include <memory>
import OSFrameworkWindows10Module;
using impl = struct OSFramework::impl {
impl();
~impl();
};
impl::impl()
{}
impl::~impl() = default;
OSFramework::OSFramework() :_impl{ std::make_unique<impl>() } {}
OSFramework::~OSFramework() = default;
compiler output
1>------ Build started: Project: Vegas22Metroidvania1, Configuration: Debug x64 ------
1>Scanning sources for module dependencies...
1>OSFrameworkWindows10Module.ixx
1>Compiling...
1>OSFrameworkWindows10Module.ixx
1>OSFrameworkWindows10.cpp
1>winmain.cpp
1>Generating Code...
1>Vegas22Metroidvania1.vcxproj -> D:\projects\Vegas22Metroidvania1\x64\Debug\Vegas22Metroidvania1.exe
I want to thank @alanbirtles for the tip about the inline
keyword when used in a header file, because this got me thinking about how I would normally do this just using a header file and a source file. This allowed me to re-read some blog posts about separating module interface and implementation and better understand what was being discussed.
CodePudding user response:
This feels like a compiler bug. Here are a list of things that make the compiler cooperate, but none of them should matter:
- Declaring the destructor declaration
inline
(inline ~OSFramework();
). - Defining the out-of-line destructor
inline
(inline OSFramework::~OSFramework() = default;
) - Defining the out-of-line destructor with
= {}
instead of= default;
. - Moving the
= default;
constructor back to within the class definition.
None of these should fix anything. Member functions in modules are not implicitly inline
, but that shouldn't cause a multiple-definition error. Importing a module is not like including a header.
And changing from = default;
to = {}
shouldn't change whether multiple definitions are created.
No, this is a compiler bug. I'd say that you should just = default
it inline. It makes the code clearer anyway.