Question

At building I get the following error:

main.obj : error LNK2019: unresolved external symbol ""public: __cdecl Worker::Worker(void)" (??0Worker@@QEAA@XZ)" in function "main".

main.obj : error LNK2019: unresolved external symbol ""public: virtual __cdecl Worker::~Worker(void)" (??1Worker@@UEAA@XZ)" in function "main".

I cannot find the Problem. (I also looked here)

main.cpp

#include <iostream>
#include <thread>
#include "worker.h"

using namespace std;

void pause_thread(int n)
{
    std::this_thread::sleep_for (std::chrono::seconds(n));
    std::cout << "pause of " << n << " seconds ended\n";
}

int main()
{
    std::cout << "Spawning and detaching 3 threads...\n";
    std::thread (pause_thread,1).detach();
    std::thread (pause_thread,2).detach();
    std::thread (pause_thread,3).detach();
    std::cout << "Done spawning threads.\n";

    std::cout << "(the main thread will now pause for 5 seconds)\n";
    // give the detached threads time to finish (but not guaranteed!):
    pause_thread(5);

    Worker w;



    return 0;
}

worker.h

#ifndef WORKER_H
#define WORKER_H


#include "jobqueue.h"
#include "job.h"
#include <mutex>
#include <thread>

using namespace std;

class Worker
{
private:
    JobQueue jobs;
    mutex mu;
    thread & workerThread;
    bool stop;

    void work();

public:
    Worker();
    virtual ~Worker();
    void addJob(Job*);
    int getJobCount();


};

#endif // WORKER_H

worker.cpp

#include "worker.h"

Worker::Worker(): workerThread(work), stop(false)
{
}

Worker::~Worker()
{
    workerThread.join();
}

void Worker::work(){
    while (!stop) {
        unique_lock<mutex> lock(mu, defer_lock);
        lock.lock();
            Job* job = jobs.getNextJob();
        lock.unlock();

        job->run();
        delete job;
    }
}

void Worker::addJob(Job* job){
    jobs.append(job);
}

int Worker::getJobCount(){
    unique_lock<mutex> lock(mu);
    return jobs.size();
}

project.pro

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt

SOURCES += main.cpp \
    jobqueue.cpp \
    worker.cpp

HEADERS += \
    jobqueue.h \
    worker.h \
    job.h

Deleting the Project.pro.user solves the (main-) problem, now errors are shown again

Was it helpful?

Solution

There are many mistakes in your code, as I suggested you should learn more C++ basics before this.
As I showed the mistakes in my comments, let me answer your problem with the member function only:

C++ treats functions as first class citizens, as opposed to Java (Java 8 improvements fix some issues, but it does not treat functions as first class citizens neither).
C++ understands functions as a concept of callable entity: A callable entity is anything that could be called, i.e. treated as a function. So a callable entity could be:

  • A global function: Just the good old C function. It could be defined, implemented, and called:

    void f() {}
    
    int main()
    {
        f(); //Call to f
    }
    
  • A member function: The classic OO member function. Its called within an object, and operates on its data:

    struct foo
    {
        void f();   
    };
    
    int main()
    {
        foo myfoo;
    
        myfoo.f(); //Call to foo::f
    }
    
  • A static member function: Its a member function not linked to an object, it operates at class level, so its signature is the same as the signature of a global function (Remember this, its important as we will see later)

    struct foo
    {
        static void f();
    {
    
    int main()
    {
        foo::f(); //Call to foo::f
    }
    
  • A functor: A functor is just a class which objects are designed to work as they where functions. Thats achieved overloading the () operator:

    struct f
    {
        void operator()() const
        {}
    };
    
    int main()
    {
        f myf;
    
        myf(); //Call to foo
    }
    

    The standard library defines the template std::function, which is a type erased functor designed to hold any kind of callable entity:

    #include <functional>
    
    void f() {}
    
    int main()
    {
        std::function<void()> f_wrapper;
    
        f_wrapper(); //Call to f_wrapper, which is an indirect call to f
    } 
    
  • A lambda expression: A lambda expression is just an inplace-defined anonymous functor:

    int main()
    {
        std::function<void()> lambda = [](){ std::cout << "hello!"; };
    }
    

    Hello!

  • A pointer to a function: C allowed the user to store functions via function pointers, just like it allows store pointers to data. C++ has the same capabilities, extended for member functions too:

    void f() {}
    void g( void(*function)() ) 
    { 
        function(); //Call to the function referenced by the pointer passed as parameter 
    }
    
    int main()
    {
        g(f); //Call to g passing f as parameter. Its an indirect call to f. Note that the & is not needed
    }
    

    As we said above, static member functions have the same signature as global functions, so the syntax is exactly the same as in the example above.
    But for member functions is not the same: A member function is linked to an object, so its called within an object. The syntax of a member function pointer is as follows:

    struct foo
    {
        voif f();
    };
    
    typedef void(foo::* pointer_to_f_type)();
    
    int main()
    {
        pointer_to_f_pointer ptr = &foo::f; //Note that the & is needed, just like in variable pointers
        foo myfoo;
    
        (myfoo.*ptr)(); //Call to the foo member function pointed by ptr (foo::f) using myfoo as object
    }
    

    A member function pointer of an specific signature has nothing to do with a pointer to a global/static function of the same signature, and cannot be convertible from/to member-pointer to non member pointer and vice-versa.

    Because function pointers and member function pointers are completely separated things, we cannot treat any kind of function in an homogeneous way directly. For example, we cannot make an array which holds both function pointers and member function pointers. However, the standard library provides the function template std::bind, which allows us to bind a function to some (or all) call parameters. That is, the object returned by std::bind() represents a partial (or complete) call to a callable entity.
    For example:

     void f( int , int , int ) {}
    
     int main()
     {
         std::function<void(int,int,int)> f_wrapper = f;
    
         f(1,2,3); //Ok
         f_wrapper(1,2,3); //Ok
    
         std::function<void()> f_call = std::bind( f , 1 , 2 , 3 ); //f_call represents a partial call (Complete in this case) fo f
         f_call(); //Execute the call
    
         std::function<void(int)> partial_f_call = std::bind( f , std::placeholders::_1 , 2 , 3 );
         partial_f_call( 1 ); //Same execution as above
     } 
    

    As you can see, std::bind() allows us to bind certain parameters to a function, making a callable entity which represents a call to the function. So it could be used to bind an object to a member function, making a callable entity with exactly the same form of a std::function instance initialized with any other kind of callable entity. That is we could use std::function to store member and non-member functions in the same way, and use it in the same way**:

     void f();
    
     struct foo
     {
         void f();
     };
    
     int main()
     {
         std::vector<std::function<void()>> functions;
         foo myfoo;
    
         functions.push_back( f );
         functions.push_back( std::bind( &foo::f , myfoo ) );
         functions.push_back( [](){} );
         ...
    
         for( const auto& function : functions )
             function();
     }
    

As you can see, there are many forms of callable entities. One important point is that someone could use C++ templates and rely on duck typing to use a callable entity passed as parameter:

 template<typename F>
 void call_function( const F& function )
 {
     function(); //function should be any kind of thing which could be called, that is, a callable entity
 } 

That's exactly what the std::thread constructor does. It just takes any kind of callable entity, a set of parameters for the call, starts a new thread, and later the callable entity is called on that new thread (Via join() or detach(). Its implementation could be something like:

template<typename F , typename... ARGS>
thread::thread( F&& function , ARGS&&... args )
{
    _thread = create_thread();

    _function = std::bind( std::forward<F>( function ) , std::forward<ARGS>( args )... ); 
}


void thread::detach()
{
    detach_thread( _thread );
    _function();
}

void thread::join()
{
    join_thread( _thread );
    _function();
}

Of course this is not a working implementation, is just an overview :p

So now you can understand why your approach doesn't work, and what you could do to solve that. Specifically, use std::bind() to create a callable entity if you want to use a member function on the thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top