kovac
kovac

Reputation: 5389

Linker error: Failing to resolve constructor

New to cpp. Strange this is I actually managed to compile this the day before. But suddenly it's not compiling today, probably because of some caching. Once I clear CMake cache, I started getting all kinds of errors...

I defined a class like:

#pragma once

#include <string>
#include <memory>

#include <boost/uuid/uuid.hpp>

#include "lib/Msg.hpp"
#include "lib/SplitParts.hpp"

namespace blz
{
    class SmsMsg : Msg
    {
    public:
        enum class SmsCoding { Undefined = -1, SevenBit, EightBit, Ucs2 };

        enum class DcsEncodeMode { Undefined = -1, Default, Fx };

        enum class Mwi { Undefined = -1, VoiceOn, FaxOn, EmailOn, OtherOn, VoiceOff, FaxOff, EmailOff, OtherOff };

        enum class Mclass { Undefined = -1, Zero, One, Two, Three };

        SmsMsg(boost::uuids::uuid id, int smsType, std::string& sender, std::string& receiver,
               std::string& udhData, std::string& msgData, std::string& smscId, std::string& smscNumber,
               std::string& foreignId, std::string& service, std::string& account, int time, Mclass mclass,
               Mwi mwi, SmsCoding coding, bool compress, int validity, int deferred, int dlrMask, std::string& dlrUrl,
               int pid, int altDocs, int rpi, std::string& charset, std::string& boxId, std::string& binInfo,
               int msgLeft, std::unique_ptr<SplitParts> splitParts, int priority, int resendTry, int resendTime,
               std::string& metaData);

        int encodeDcs(DcsEncodeMode mode);

    private:
        boost::uuids::uuid id;
        int smsType;
        std::string sender;
        std::string receiver;
        std::string udhData;
        std::string msgData;
        std::string smscId;
        std::string smscNumber;
        std::string foreignId;
        std::string service;
        std::string account;
        int time;
        Mclass mclass;
        Mwi mwi;
        SmsCoding coding;
        bool compress;
        int validity;
        int deferred;
        int dlrMask;
        std::string dlrUrl;
        int pid;
        int altDocs;
        int rpi;
        std::string charset;
        std::string boxId;
        std::string binfo;
        int msgLeft;
        std::unique_ptr<SplitParts> splitParts{};
        int priority;
        int resendTry;
        int resendTime;
        std::string metaData;
    };
}

Implementation of the class is as below:

#include "SmsMsg.hpp"

inline blz::SmsMsg::SmsMsg(const boost::uuids::uuid id, const int smsType, std::string& sender, std::string& receiver,
                           std::string& udhData, std::string& msgData, std::string& smscId, std::string& smscNumber,
                           std::string& foreignId, std::string& service, std::string& account, const int time,
                           const Mclass mclass, const Mwi mwi, const SmsCoding coding, const bool compress,
                           const int validity, const int deferred, const int dlrMask, std::string& dlrUrl,
                           const int pid, const int altDocs, const int rpi, std::string& charset, std::string& boxId,
                           std::string& binInfo, const int msgLeft, std::unique_ptr<SplitParts> splitParts,
                           const int priority, const int resendTry, const int resendTime, std::string& metaData) :
    id(id),
    smsType(smsType),
    sender(sender),
    receiver(receiver),
    udhData(udhData),
    msgData(msgData),
    smscId(smscId),
    smscNumber(smscNumber),
    foreignId(foreignId),
    service(service),
    account(account),
    time(time),
    mclass(mclass),
    mwi(mwi),
    coding(coding),
    compress(compress),
    validity(validity),
    deferred(deferred),
    dlrMask(dlrMask),
    dlrUrl(dlrUrl),
    pid(pid),
    altDocs(altDocs),
    rpi(rpi),
    charset(charset),
    boxId(boxId),
    binfo(binInfo),
    msgLeft(msgLeft),
    splitParts(std::move(splitParts)),
    priority(priority),
    resendTry(resendTry),
    resendTime(resendTime),
    metaData(metaData)
{
}

int blz::SmsMsg::encodeDcs(const DcsEncodeMode mode)
{
    auto dcs = 0;

    if (coding == SmsCoding::Undefined)
    {
        coding = udhData.length() ? SmsCoding::EightBit : SmsCoding::SevenBit;
    }

    if (mwi != Mwi::Undefined)
    {
        dcs = static_cast<int>(mwi);

        if (dcs & 0x04)
        {
            dcs = (dcs & 0x03) | 0xC0;
        }
        else
        {
            dcs = (dcs & 0x03) | 0x08;
            dcs |= !msgData.length() ? 0xC0 : coding == SmsCoding::SevenBit ? 0xD0 : 0xE0;
        }
    }
    else
    {
        if (mode == DcsEncodeMode::Default || mode == DcsEncodeMode::Undefined || coding == SmsCoding::Ucs2 || compress)
        {
            if (compress)
            {
                dcs |= 0x20;
            }

            if (mclass != Mclass::Undefined)
            {
                dcs |= (0x10 | static_cast<int>(mclass));
            }

            if (coding != SmsCoding::Undefined)
            {
                dcs |= (static_cast<int>(coding) << 2);
            }
        }
        else
        {
            dcs |= 0xF0;

            if (coding != SmsCoding::Undefined)
            {
                dcs |= (static_cast<int>(coding) << 2);
            }

            dcs |= mclass == Mclass::Undefined ? 1 : static_cast<int>(mclass);
        }
    }

    return dcs;
}

Msg.hpp and SplitParts.hpp are just empty classes with no implementation like class Msg {} and class SplitParts {}.

Then I have a test class like:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "SmsMsg.hpp"
#include "lib/SplitParts.hpp"

blz::SmsMsg getTestSms()
{
    auto msgId = boost::uuids::uuid();
    auto smsType = 1;
    std::string sender = "blz";
    std::string receiver = "48765432";
    std::string udhData = "udhData";
    std::string msgData = "Hello Foo!";
    std::string smscId = "smscId";
    std::string smscNumber = "smscNo";
    std::string foreignId = "foreignId";
    std::string service = "service";
    std::string account = "account";
    std::string dlrUrl = "http://example.com/dlr";
    std::string charset = "ascii";
    std::string boxId = "boxId";
    std::string binInfo = "binInfo";
    std::string metaData = "metaData";
    auto time = 26748590;
    auto mclass = blz::SmsMsg::Mclass::One;
    auto mwi = blz::SmsMsg::Mwi::VoiceOn;
    auto coding = blz::SmsMsg::SmsCoding::EightBit;
    auto compress = true;
    auto validity = 1;
    auto deferred = 0;
    auto dlrMask = 1;
    auto pid = 1;
    auto altDocs = 1;
    auto rpi = 1;
    auto msgLeft = 1;
    auto splitParts = std::make_unique<blz::SplitParts>();
    auto priority = 1;
    auto resendTry = 1;
    auto resendTime = 786545367;

    return blz::SmsMsg(msgId, smsType, sender, receiver, udhData, msgData, smscId, smscNumber, foreignId, service,
                       account, time, mclass, mwi, coding, compress, validity, deferred, dlrMask, dlrUrl, pid, altDocs,
                       rpi, charset, boxId, binInfo, msgLeft, std::move(splitParts), priority, resendTry, resendTime, metaData);
}

TEST_CASE("Encode DCS using sms fields", "[sms]")
{
    auto sms = getTestSms();

    REQUIRE(sms.encodeDcs(blz::SmsMsg::DcsEncodeMode::Default) == 1);
}

My project structure:

project
  |-------src
           |------lib
                   |------ Msg.cpp
                   |------ Msg.hpp
                   |------ SplitParts.hpp
           |------ SmsMsg.cpp
           |------ SmsMsg.hpp
   ------ tests
           |------ SmsMsgTest.cpp
   ------ CMakeLists.txt

Content of the CMakeLists.txt:

cmake_minimum_required(VERSION 3.15)

project(Blaze)

set(BLZ_HEADERS
    "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Msg.hpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/SplitParts.hpp"

    "${CMAKE_CURRENT_SOURCE_DIR}/src/SmsMsg.hpp"
)

set(BLZ_SOURCES
    "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Msg.cpp"

    "${CMAKE_CURRENT_SOURCE_DIR}/src/SmsMsg.cpp"
)

set(BLZ_TESTS
    "${CMAKE_CURRENT_SOURCE_DIR}/tests/Setup.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/tests/SmsMsgTest.cpp"
)

set(Boost_USE_STATICLIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)

find_package(Boost 1.72.0 COMPONENTS system REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})

add_library(Blz STATIC ${BLZ_HEADERS} ${BLZ_SOURCES})

target_link_libraries(Blz ${Boost_LIBRARIES})

### Tests
enable_testing()

find_package(Catch2 REQUIRED)
add_executable(BlzTests ${BLZ_TESTS})
target_include_directories(BlzTests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src")
target_include_directories(BlzTests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src/lib")

target_link_libraries(BlzTests Blz)
target_link_libraries(BlzTests Catch2::Catch2)

include(CTest)
include(Catch)

catch_discover_tests(BlzTests)

The error I'm getting is:

Error   LNK2019 unresolved external symbol "public: __cdecl blz::SmsMsg::SmsMsg(struct boost::uuids::uuid,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,int,enum blz::SmsMsg::Mclass,enum blz::SmsMsg::Mwi,enum blz::SmsMsg::SmsCoding,bool,int,int,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,int,int,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,int,class std::unique_ptr<class blz::SplitParts,struct std::default_delete<class blz::SplitParts> >,int,int,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &)" (??0SmsMsg@blz@@QEAA@Uuuid@uuids@boost@@HAEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@11111111HW4Mclass@01@W4Mwi@01@W4SmsCoding@01@_NHHH1HHH111HV?$unique_ptr@VSplitParts@blz@@U?$default_delete@VSplitParts@blz@@@std@@@6@HHH1@Z) referenced in function "class blz::SmsMsg __cdecl getTestSms(void)" (?getTestSms@@YA?AVSmsMsg@blz@@XZ)

I'm using Visual Studio IDE CMakeconfig:

{
  "configurations": [
    {
      "name": "x64-Debug",
      "generator": "Ninja",
      "configurationType": "Debug",
      "inheritEnvironments": ["msvc_x64_x64"],
      "buildRoot": "${projectDir}\\out\\build\\${name}",
      "installRoot": "${projectDir}\\out\\install\\${name}",
      "cmakeCommandArgs": "",
      "buildCommandArgs": "-v",
      "ctestCommandArgs": "",
      "cmakeToolchain": "C:/Users/xxxx/.vcpkg/scripts/buildsystems/vcpkg.cmake",
      "variables": []
    }
  ]
}

Upvotes: 1

Views: 225

Answers (1)

Igor Tandetnik
Igor Tandetnik

Reputation: 52611

[basic.def.odr]/4 ... An inline function or variable shall be defined in every translation unit in which it is odr-used outside of a discarded statement.

The definition of SmsMsg is marked inline, but it's not visible in the translation unit where it's used. Your program violates the one definition rule, and is therefore ill-formed.

inline keyword is normally used with functions defined in headers. It makes little sense to put it on a function definition in a source file.

Upvotes: 6

Related Questions