0

I've been sitting here puzzled for a while now, I've read through many blogs and forms trying to understand this error and how to fix it, no luck sadly.

I am getting this error every time I try and call Log::out();

Error LNK2019 unresolved external symbol "public: static void __cdecl Engine::Log::out<char const *>(enum Engine::Log::LogLevel,char const *)" (??$out@PEBD@Log@Engine@@SAXW4LogLevel@01@PEBD@Z) referenced in function "public: __cdecl Game::Game(void)" (??0Game@@QEAA@XZ) GameEngine C:\Users\Owner\Desktop\Projects\GameEngine\GameEngine\Game.obj 1

This is the Log.h file:

namespace Engine
{
    class Log
    {
    public:
        static HANDLE hConsole;

        enum class LogLevel
        {
            Info, Debug, Warn, Error
        };

        template<typename T>
        static void out(LogLevel level, T data);
    };
}

and Log.cpp

#include "Log.h"

namespace Engine
{
    HANDLE Log::hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    template<typename T>
    void Log::out(Log::LogLevel level, T data)
    {
        switch (level)
        {
        case Log::LogLevel::Info:
            SetConsoleTextAttribute(Log::hConsole, 7);
            break;
        case Log::LogLevel::Debug:
            SetConsoleTextAttribute(Log::hConsole, 10);
            break;
        case Log::LogLevel::Warn:
            SetConsoleTextAttribute(Log::hConsole, 14);
            break;
        case Log::LogLevel::Error:
            SetConsoleTextAttribute(Log::hConsole, 12);
            break;
        default:
            SetConsoleTextAttribute(Log::hConsole, 7);
            break;
        }

        std::cout << data << std::endl;

        SetConsoleTextAttribute(Log::hConsole, 7);
    }
}

I'm calling it in the main function, like so: Engine::Log::out(Engine::Log::LogLevel::Debug, "Test");

Any help would be apricated, thank you.

Callum S
  • 213
  • 2
  • 10
  • Did you read this [linker tools error](https://learn.microsoft.com/en-us/cpp/error-messages/tool-errors/linker-tools-error-lnk2019?view=msvc-160)? – Lazar Đorđević Feb 11 '21 at 11:08
  • The basic concept with regards to template is that definition of the template must be known before instantiating it, in your case you are doing it after calling GetStdHandle. If there are only these two files, I think if you move the member function template before to definition of the static it might work. But I don't know how GetStdHandle works. – Anirudh Feb 11 '21 at 11:16

1 Answers1

0

When dealing with template, the easiest way is to provide them inline.

This way, all the specific code can be generated at call site (instanciation).

Without that, what would be the code generated from Log.cpp without knowing T yet?

namespace Engine
{
    class Log
    {
    public:
        static HANDLE hConsole;

        enum class LogLevel
        {
            Info, Debug, Warn, Error
        };

        template<typename T>
        static void out(LogLevel level, T data)
        {
        switch (level)
        {
        case Log::LogLevel::Info:
            SetConsoleTextAttribute(Log::hConsole, 7);
            break;
        case Log::LogLevel::Debug:
            SetConsoleTextAttribute(Log::hConsole, 10);
            break;
        case Log::LogLevel::Warn:
            SetConsoleTextAttribute(Log::hConsole, 14);
            break;
        case Log::LogLevel::Error:
            SetConsoleTextAttribute(Log::hConsole, 12);
            break;
        default:
            SetConsoleTextAttribute(Log::hConsole, 7);
            break;
        }

        std::cout << data << std::endl;

        SetConsoleTextAttribute(Log::hConsole, 7);
        }
    };
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30