I want to setup a QueueHandler that organizes function calls of an arbitrary class. In my case, I have a UDP interface that should not be overloaded. Thus, the I have a queue of function calls, so that a UDP request is sent only every N milliseconds (or seconds). When try to add functions to the queue, I get the LNK2019 error that there is an unresolved external symbol (as far as I understand this error, this means that there is a declared symbol, but it is not defined). However, to illustrate the problem, I broke it down to the basic principle that reproduces the error:
QueueHandler
/* =============== Header file QueueHandler.h ============================= */
#pragma once
#include <QThread>
#include <queue>
#include <functional>
#include <iostream>
#include <chrono>
class QueueHandler : public QThread
{
Q_OBJECT
public:
QueueHandler(QObject* parent=nullptr);
~QueueHandler() = default;
void run() override;
template<typename _Callable, typename... Args>
void QueueFunction(_Callable&& __f, Args&&... args);
private:
uint m_iSleepSec{ 2 };
std::queue<std::function<void()>> funcs;
};
/* =============== implementation QueueHandler.cpp ============================= */
//#include "QueueHandler.h"
QueueHandler::QueueHandler(QObject* parent /*= nullptr*/)
{}
void QueueHandler::run()
{
bool bRunning = true;
while (bRunning)
{
if (!funcs.empty())
{
std::function<void()> func = funcs.front();
funcs.pop();
func();
}
else
{
std::cout << "No command available.\n";
}
std::cout << "Sleep for " << m_iSleepSec << "seconds...";
this->msleep(m_iSleepSec * 1000);
std::cout << "... proceed processing. \n ";
}
}
template<typename _Callable, typename... Args>
void QueueHandler::QueueFunction(_Callable&& __f, Args&&... args)
{
std::function<void()> func = std::bind(std::forward<_Callable>(__f), std::forward<Args>(args)...);
funcs.push(func);
}
Test class that filles the Queue:
/* Header file LpTest.h*/
/* =============== implementation LpTest.cpp ============================= */
#pragma once
#include "QueueHandler.h"
#include <QtCore>
#include <memory>
class LpTest : public QObject
{
Q_OBJECT
public:
LpTest();
~LpTest() = default;
void fillQueue();
void testFunction(int iIn);
void ping();
signals:
void resultReady(QVariant var);
private:
std::unique_ptr<QueueHandler> m_pQueueHandler{};
};
/* =============== implementation LpTest.cpp ============================= */
//#include "LpTest.h"
LpTest::LpTest()
{
m_pQueueHandler = std::make_unique<QueueHandler>(this);
}
void LpTest::fillQueue()
{
m_pQueueHandler->QueueFunction(&LpTest::testFunction, 2);
}
void LpTest::testFunction(int iIn)
{
std::cout << "testFunction: " << iIn << "\n";
}
void LpTest::ping()
{
std::cout << "ping\n";
}
The Main.cpp looks just like:
#include <QtCore/QCoreApplication>
#include "LpTest.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
LpTest myLp{};
myLp.fillQueue();
return a.exec();
}
The error occurs when calling fillQueue() from the LpTest class.
Btw, when the queue is defined within the LpTest class, everything works fine. But I want to put it into another class, so I might reuse the QueueHandler later on.
The exact error is:
error LNK2019: unresolved external symbol "public: void __cdecl QueueHandler::QueueFunction<void (__cdecl QueueHandler::*)(class QString),char const (&)[6]>(void (__cdecl QueueHandler::*&&)(class QString),char const (&)[6])" (??$QueueFunction@P8QueueHandler@@EAAXVQString@@@ZAEAY05$$CBD@QueueHandler@@QEAAX$$QEAP80@EAAXVQString@@@ZAEAY05$$CBD@Z) referenced in function "public: void __cdecl LpTest::fillQueue(void)" (?fillQueue@LpTest@@QEAAXXZ)
Thanks in advance!
I have red the error guide for LNK2019 by Microsoft, but could not adopt it to my problem, since I think, everything is defined where it should be. Microsoft Linker Tools Error LNK2019
Problem solved. It is not allowed to define template functions in the cpp file. I have moved the definition of QueueFunction to the header file (ugly but that seems to be the way to go...)
Edit: additionally the
QueueFuntionis slightly wrong. Thestd::bindneeds to know the "caller" of the function. The correct implementation looks like:and is corespondingly called with: