Home > Blockchain >  "multiply defined symbols found" how is my destructor being defined twice?
"multiply defined symbols found" how is my destructor being defined twice?

Time:12-18

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.

  • Related