1
votes

I'm creating a simple debug thread name database (because you can't give thread a name in windows, you can only send that name to debugger). I have this header file:

threadname.h

class ThreadNameMap {
public:
    void setThreadName( uint32_t id, const std::string &name );

    std::string getThreadName( const uint32_t id ) const;
    // If ID given as string
    std::string getThreadName( const std::string id ) const;
    // If no ID given, current ID is used
    std::string getThreadName( ) const;

    // A singleton getter
    static ThreadNameMap* ThreadNameMap::getInstance();
    // Static getters that use the singleton
    static std::string getName( const uint32_t id );
    static std::string getName( const std::string id );
    static std::string getName( );
private:
    ThreadNameMap() {maxNameLength = 16;};
    int maxNameLength;
    // Map of ids and names
    std::map<uint32_t, std::string> names;
}; 

This is the implementation:

threadname.cpp

ThreadNameMap* ThreadNameMap::getInstance() {
    static ThreadNameMap inst = ThreadNameMap();
    return &inst;
}

std::string ThreadNameMap::getName( const uint32_t id ) { return getInstance()->getThreadName(id); }
std::string ThreadNameMap::getName( const std::string id ) { return getInstance()->getThreadName(id); }
std::string ThreadNameMap::getName( ) { return getInstance()->getThreadName(); }

void ThreadNameMap::setThreadName( uint32_t id, const std::string &name ) {
    setThreadName_private(id, name.c_str());
    if(id==-1)
        id = boostThreadId();
    names[id] = name;
}

std::string ThreadNameMap::getThreadName( const uint32_t id ) const {
    if( names.count( id )>0)
        return names.at(id);
    else
        return "";
}
std::string ThreadNameMap::getThreadName( const std::string id ) const {
    uint32_t threadNumber = 0;
    sscanf(id.c_str(), "%lx", &threadNumber);
    return getThreadName(threadNumber);
}
/** THIS ONE IS REPORTED AS UNREFERENCED!!! **/
std::string ThreadNameMap::getThreadName( ) const {
    return getThreadName(boostThreadId());
}

I use singleton interface and I access it like this in my logger header file:

logger.h

#include "... path .../setthreadname.h"
#define LOGMTDBG_tmp(debuglevel, logstream) LOGMT(debugLevel(debuglevel) << datetimeEx << ' ' << ThreadNameMap::getName() << ' ' << __FUNCTION__ << ' ' << logstream)

Logger is included in many and many other cpp and h files - wherever needed. And some of those report unreferenced symbol:

Error   13  error LNK2001: unresolved external symbol "public: class std::basic_string,class std::allocator > __cdecl ThreadNameMap::getThreadName(void)" (?getThreadName@ThreadNameMap@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)   D:\techsys\RomeoTT\Source\RunWindow.obj
Error   14  error LNK2019: unresolved external symbol "public: class std::basic_string,class std::allocator > __cdecl ThreadNameMap::getThreadName(void)" (?getThreadName@ThreadNameMap@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) referenced in function "private: void __cdecl BaseRunPresenter::MessageHandler(class boost::shared_ptr)" (?MessageHandler@BaseRunPresenter@@AEAAXV?$shared_ptr@USMSSBase@@@boost@@@Z)    D:\techsys\RomeoTT\Source\BaseRunPresenter.obj
Error   15  error LNK2001: unresolved external symbol "public: class std::basic_string,class std::allocator > __cdecl ThreadNameMap::getThreadName(void)" (?getThreadName@ThreadNameMap@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)   D:\techsys\RomeoTT\Source\BaseRunView.obj
Error   16  error LNK2001: unresolved external symbol "public: class std::basic_string,class std::allocator > __cdecl ThreadNameMap::getThreadName(void)" (?getThreadName@ThreadNameMap@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)   D:\techsys\RomeoTT\Source\XmlSestavaRunPresenter.obj
Error   17  error LNK2001: unresolved external symbol "public: class std::basic_string,class std::allocator > __cdecl ThreadNameMap::getThreadName(void)" (?getThreadName@ThreadNameMap@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)   D:\techsys\RomeoTT\Source\RunSestavaFrame.obj
Error   18  error LNK2019: unresolved external symbol "class std::basic_string,class std::allocator > __cdecl getThreadName(void)" (?getThreadName@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ) referenced in function "public: void __cdecl SestavaHeader::DataSource::Dump2LOG(void)const " (?Dump2LOG@DataSource@SestavaHeader@@QEBAXXZ)  D:\techsys\RomeoTT\Source\SestavaDataSource.obj

I searched the project, but there is not a single reference of any of the class non static methods, only the one in logger.h.

I checked many of the Unreferenced external symbol questions, but they all came with two possibilities:

  1. dll is missing - well, I'm not using any dll for this obviously
  2. Function was declared but not implemented - in the code above you can see both declaration and implementation and they are matching

So why would linker complain about method that is not even referenced? I tried to alter const and move static method in .h file, nothing helped. I'm really desperate now.

Edit: Visual studio linker flags:

/OUT:"D:\techsys\RomeoTT\PROJECT\PROJECTd.exe" /INCREMENTAL /NOLOGO /LIBPATH:"..\..\libs\openssl64\lib\VC\static" /LIBPATH:"..\..\libs\boost155\stage\lib64bit" /LIBPATH:"C:\Qt\5.3.0-64\qtbase\lib" "libeay32MTd.lib" "Graph64d.lib" "HelpLib64d.lib" "version.lib" "qtmaind.lib" "Qt5Cored.lib" "Qt5Guid.lib" "Qt5Multimediad.lib" "Qt5Sqld.lib" "Qt5PrintSupportd.lib" "Qt5Svgd.lib" "Qt5Widgetsd.lib" "Qt5Xmld.lib" "Qt5XmlPatternsd.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /MANIFEST /ManifestFile:"D:\techsys\XXXX\obj\x64\XXXX\Debug\XXXXXd.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"D:\techsys\XXXXX\XXXXX64\XXXXXd.pdb" /SUBSYSTEM:WINDOWS /OPT:NOREF /PGD:"D:\techsys\XXXX\XXXX64\XXXXXd.pgd" /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X64 /ERRORREPORT:NONE 
1
And you do build with threadname.cpp and link with the object file generated by the source file?Some programmer dude
have a look at the source code the linker is complaining about. This appears to be calling getThreadName() directly (your logger is calling getName). Also one of your files is callin getThreadName without a class name (last line of the error)Tom Tanner
It looks like the files RunWindow.obj et al is referring to the symbol. How about sharing the offending source? By the way how about a minimal, complete, verifiable example (stackoverflow.com/help/mcve)? Preparing a MCVE itself could in fact reveal the problem.skyking
I still think it is related to the implementation being a const member function, while the header advertises a non-const member function. There is no implementation for a non-const getName().Peter - Reinstate Monica
@TomášZato That's even more reason to create a minimal, complete, verifiable example. If you're so sure that the reference come from the posted source then you should be able to pinpoint your problem, but you obviously haven't since you ask. If it were that all references were in the posted code you should be able to just wrap it up and make it complete (and see if the problem is verifiable in that example).skyking

1 Answers

2
votes

You find the likely culprit by examining in detail the error messages (cleaned-up for lisibility):

Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in RunWindow.obj
Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in function "private: void stringBaseRunPresenter::MessageHandler(class boost::shared_ptr)" referenced in BaseRunPresenter.obj
Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in BaseRunView.obj
Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in XmlSestavaRunPresenter.obj
Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in RunSestavaFrame.obj
Unresolved external symbol "string stringThreadNameMap::getThreadName(void)" referenced in function "public: void stringSestavaHeader::DataSource::Dump2LOG(void)const " SestavaDataSource.obj

What these linker errors say is that:

  • Some object files (RunWindow.obj, BaseRunPresenter.obj...) contain a reference to a non-const method getThreadName()
  • It implies that these object files have been generated from source files at a time when the method getThreadName() was declared non-const

However, the declaration of getThreadName() is now const:

std::string getThreadName( ) const;

Thus, each of these object files have been compiled based on an obsolete declaration of getThreadName() and should be recompiled. If possible, as there may be other (undetected) inconsistency errors, you should perform a full rebuild.