Attempting to use Templates and a class I define not compiling/loading

I am learning a bit of thread programming via this book C ++ Concurrency in Action. In the current place I am stuck, I am trying to test out thread safe object pools..

https://github.com/anthonywilliams/ccia_code_samples/blob/main/listings/listing_9.1.cpp

Jumping from example 9.1 to 9.2.. they kind of reference eachother...
https://github.com/anthonywilliams/ccia_code_samples/blob/main/listings/listing_9.2.cpp

I'm trying to pull out the function_wrapper class in Listing 9.2 into a .cpp and .hpp of its own.. but when I goto put that class into the type of threadsafe_queue class Listing 9.1.. I get an error which I do not quite grasp.. I Know it is something to do where perhaps I need to explicitly tell the compiler to instantiate the fuction_wrapper with some sort of... template examples?

When I compile with CMake I get the following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
% sh compile.sh
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Current conanbuildinfo.cmake directory: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
Scanning dependencies of target test_cpp_multi
[ 25%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/function_wrapper.cpp.o
[ 50%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/threadsafe_queue.cpp.o
[ 75%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/helloworld.cpp.o
[100%] Linking CXX executable bin/test_cpp_multi
Undefined symbols for architecture x86_64:
  "function_wrapper::function_wrapper(function_wrapper const&)", referenced from:
      threadsafe_queue<function_wrapper>::push(function_wrapper) in threadsafe_queue.cpp.o
      void std::__1::deque<function_wrapper, std::__1::allocator<function_wrapper> >::__append<std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l> >(std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::enable_if<__is_cpp17_forward_iterator<std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l> >::value, void>::type*) in threadsafe_queue.cpp.o
  "function_wrapper::function_wrapper()", referenced from:
      thread_pool::worker_thread() in helloworld.cpp.o
  "function_wrapper::function_wrapper<std::__1::packaged_task<void ()> >(std::__1::packaged_task<void ()>&&)", referenced from:
      std::__1::future<std::__1::result_of<void (* ())()>::type> thread_pool::submit<void (*)()>(void (*)()) in helloworld.cpp.o
  "function_wrapper::function_wrapper(function_wrapper&)", referenced from:
      threadsafe_queue<function_wrapper>::wait_and_pop() in threadsafe_queue.cpp.o
      threadsafe_queue<function_wrapper>::try_pop() in threadsafe_queue.cpp.o
  "function_wrapper::operator=(function_wrapper const&)", referenced from:
      threadsafe_queue<function_wrapper>::wait_and_pop(function_wrapper&) in threadsafe_queue.cpp.o
      threadsafe_queue<function_wrapper>::try_pop(function_wrapper&) in threadsafe_queue.cpp.o
      std::__1::__deque_iterator<function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l> std::__1::copy<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l, function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l>(std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l>) in threadsafe_queue.cpp.o
  "function_wrapper::operator()()", referenced from:
      thread_pool::worker_thread() in helloworld.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/test_cpp_multi] Error 1
make[1]: *** [CMakeFiles/test_cpp_multi.dir/all] Error 2
make: *** [all] Error 2


CMakeLists.txt
1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 2.8.12)
project(test_cpp_multi)
add_compile_options(-std=c++11)

# Using the "cmake" generator
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup(TARGETS)

add_executable(test_cpp_multi src/function_wrapper.cpp src/threadsafe_queue.cpp src/helloworld.cpp )



threadsafe_queue.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <queue>
#include <mutex>
#include <memory>
#include <condition_variable>

#include "function_wrapper.h"

//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file

template<typename T>
class threadsafe_queue
{
private:
    mutable std::mutex mut;
    std::queue<T> data_queue;
    std::condition_variable data_cond;

public:
    threadsafe_queue() {}
    threadsafe_queue(threadsafe_queue const& other)
    {
        std::lock_guard<std::mutex> lk(other.mut);
        data_queue = other.data_queue;
    }
    void push(T new_value)
    {
        std::lock_guard<std::mutex> lk(mut);
        data_queue.push(new_value);
        data_cond.notify_one();
    }
    void wait_and_pop(T &value)
    {
        std::unique_lock<std::mutex> lk(mut);
        data_cond.wait(lk, [this] { return !data_queue.empty(); });
        value = data_queue.front();
        data_queue.pop();
    }
    std::shared_ptr<T> wait_and_pop()
    {
        std::unique_lock<std::mutex> lk(mut);
        data_cond.wait(lk, [this] { return !data_queue.empty(); });
        std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
        data_queue.pop();
        return res;
    }

    bool try_pop(T &value)
    {
        std::lock_guard<std::mutex> lk(mut);
        if (data_queue.empty()) {
            return false;
        }
        value = data_queue.front();
        data_queue.pop();
        return true;
    }
    std::shared_ptr<T> try_pop()
    {
        std::lock_guard<std::mutex> lk(mut);
        if (data_queue.empty()){
            return std::shared_ptr<T>();
        }
        std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
        data_queue.pop();
        return res;
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lk(mut);
        return data_queue.empty();
    }
};


template class threadsafe_queue<std::function<void()>>;
template class threadsafe_queue<function_wrapper>;



Notice at that last line that I paste here... I thought THIS was the line that would fix my errors.. because I had similar warnings when I started using the threadsafe_queue.. I had to inject or define the <std::function<void()>> in earlier examples.. but NOW.. this is not working .. I want to know why.. but also how to better read these compiler or loader errors. Thanks!
Last edited on
For further reference.. here is my function_wrapper.cpp and .h .. I wonder if perhaps I need to repeat the lesson I learned regarding types referenced at https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file for this class as well as the threadsafe_queue??

function_wrapper.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

#include <iostream>

class function_wrapper
{
    struct impl_base {
        virtual void call()=0;
        virtual ~impl_base() {}
    };
    std::unique_ptr<impl_base> impl;
    template<typename F>
    struct impl_type: impl_base
    {
        F f;
        impl_type(F&& f_): f(std::move(f_)) {}
        void call() { f(); }
    };
public:
    template<typename F>
    function_wrapper(F&& f):
        impl(new impl_type<F>(std::move(f)))
    {}

    //void call() { impl->call(); }
    void operator()() { impl->call(); }
    
    function_wrapper() = default;

    function_wrapper(function_wrapper&& other):
        impl(std::move(other.impl))
    {}

    function_wrapper& operator=(function_wrapper&& other)
    {
        impl=std::move(other.impl);
        return *this;
    }

    function_wrapper(const function_wrapper&)=delete;
    function_wrapper(function_wrapper&)=delete;
    function_wrapper& operator=(const function_wrapper&)=delete;
};


function_wrapper.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class function_wrapper {
public:
    template<typename F>
    function_wrapper(F&& f);

    //void call() { impl->call(); }
    void operator()();
    
    function_wrapper();

    function_wrapper(function_wrapper&& other);

    function_wrapper& operator=(function_wrapper&& other);

    function_wrapper(const function_wrapper&);
    function_wrapper(function_wrapper&);
    function_wrapper& operator=(const function_wrapper&);
};


thanks again
Last edited on
You have a class function_wrapper in function_wrapper.cpp
and a different class with the same name function_wrapper in function_wrapper.hpp (this is included in threadsafe_queue.cpp)

This violates ODR; the program that links these two files together has undefined behaviour.
https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule


Thanks @JLBorges ,

I was under the impression when I want to reference a class.. I must create a .cpp and corresponding .hpp .. and in the hpp put ONLY the interface to the class (public members, public function calls) with the same name.. am I wrong ?

then to utilize this class function_wrapper, I thought i do the

1
2
3
4
5
6
7
8
9

#include "my_class.hpp"

...

...

template class threadsafe_queue<my_class>;


Edit: I am reading https://docs.microsoft.com/en-us/cpp/cpp/header-files-cpp?view=msvc-160 and thinking.. have I mistakenly made it .hpp and instead need headers to ONLY be .h filename extensions? I was under impression for C use .h , C++ .hpp ?

Edit2: Ah, now I think I see... .hpp is for definitions that are ONLY in the header file.. by sort of convention..

https://stackoverflow.com/a/20350779/389976

I will switch to .h filenames for all headers and retry. Thanks JLBorges!

Edit 3: I have renamed all .hpp files to .h , attempted a recompile.. and gotten the following still.. I guess I am missing something?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
% sh compile.sh
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Current conanbuildinfo.cmake directory: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
Scanning dependencies of target test_cpp_multi
[ 25%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/threadsafe_queue.cpp.o
[ 50%] Linking CXX executable bin/test_cpp_multi
Undefined symbols for architecture x86_64:
  "function_wrapper::function_wrapper(function_wrapper const&)", referenced from:
      threadsafe_queue<function_wrapper>::push(function_wrapper) in threadsafe_queue.cpp.o
      void std::__1::deque<function_wrapper, std::__1::allocator<function_wrapper> >::__append<std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l> >(std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::enable_if<__is_cpp17_forward_iterator<std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l> >::value, void>::type*) in threadsafe_queue.cpp.o
  "function_wrapper::function_wrapper()", referenced from:
      thread_pool::worker_thread() in helloworld.cpp.o
  "function_wrapper::function_wrapper<std::__1::packaged_task<void ()> >(std::__1::packaged_task<void ()>&&)", referenced from:
      std::__1::future<std::__1::result_of<void (* ())()>::type> thread_pool::submit<void (*)()>(void (*)()) in helloworld.cpp.o
  "function_wrapper::function_wrapper(function_wrapper&)", referenced from:
      threadsafe_queue<function_wrapper>::wait_and_pop() in threadsafe_queue.cpp.o
      threadsafe_queue<function_wrapper>::try_pop() in threadsafe_queue.cpp.o
  "function_wrapper::operator=(function_wrapper const&)", referenced from:
      threadsafe_queue<function_wrapper>::wait_and_pop(function_wrapper&) in threadsafe_queue.cpp.o
      threadsafe_queue<function_wrapper>::try_pop(function_wrapper&) in threadsafe_queue.cpp.o
      std::__1::__deque_iterator<function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l> std::__1::copy<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l, function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l>(std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper const*, function_wrapper const&, function_wrapper const* const*, long, 4096l>, std::__1::__deque_iterator<function_wrapper, function_wrapper*, function_wrapper&, function_wrapper**, long, 4096l>) in threadsafe_queue.cpp.o
  "function_wrapper::operator()()", referenced from:
      thread_pool::worker_thread() in helloworld.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/test_cpp_multi] Error 1
make[1]: *** [CMakeFiles/test_cpp_multi.dir/all] Error 2
make: *** [all] Error 2
(conan_starter) emcp@EMCPs-Mac-mini ch09_00_threadpool_simple % 


could the problem be this operator function I had in .h ??? seems I need to back off and merely mention it in the header somehow? I tried a few changes but none compiled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>

//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file



class function_wrapper {
public:
    template<typename F>
    function_wrapper(F&& f);

    void operator()();    // <---------------does this look okay?
    
    //    void operator();  <----- this errored , saying "an operator name must be declared as a function"

    function_wrapper();

    function_wrapper(function_wrapper&& other);

    function_wrapper& operator=(function_wrapper&& other);

    function_wrapper(const function_wrapper&);
    function_wrapper(function_wrapper&);
    function_wrapper& operator=(const function_wrapper&);
};

Last edited on
After some review.. I can see that maybe I was accidentally defining things between the cpp and hpp of both my classes incorrectly .. and it was only working by accident before...

I'll start reviewing this post and how to define my header and cpp properly

https://www.learncpp.com/cpp-tutorial/class-code-and-header-files/
Hi,

The file extensions for header files and implementation are purely conventions - some people name their C++ files *.cc instead of *.cpp. But it is worthwhile IMO to name C++ headers *.hpp - it means to humans that it is a C++ header file. None of this makes any difference to the compiler - one could name them *.fred or *.ginger if they wanted.

Class definitions go into header files, while the definition of the class member functions (the implementation) go into *.cpp files. Any file that uses something from a class needs to #include the header file for that class.

However, all template code (declarations and implementation) generally does have to go in the header file. You seem to have template code in all of your files.

Edit: Header files need to have header guards

Here is some really helpful reading:

http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
https://en.cppreference.com/w/
Last edited on
The file extensions for header files and implementation are purely conventions

Now with C++20 being implemented by the various compiler MS/Visual Studio made a design choice that custom module source files MUST be suffixed as .ixx files to work. Other compiler don't seem to require this.

I found a lot of module examples on the 'net and was really frustrated the examples didn't want to compile with VS 2019, even though the support was supposedly there. It took me quite some time to find out the MS .ixx mandate. *grumble*

I am tempted to use .ixx no matter what compiler I use.
Attempt #2

here are my new classes and header files.. but it does not compile.. open to feedback what could be missing. Thank you!

function_wrapper.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef FUNCTION_WRAPPER_H
#define FUNCTION_WRAPPER_H
#endif

#include <iostream>

//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file

class function_wrapper {
    struct impl_base {
        virtual void call()=0;
        virtual ~impl_base() {}
    };
    std::unique_ptr<impl_base> impl;
    template<typename F>
    struct impl_type: impl_base
    {
        F f;
        impl_type(F&& f_): f(std::move(f_)) {}
        void call() { f(); }
    };
public:
    template<typename F>
    function_wrapper(F&& f);

    //void call() { impl->call(); }
    void operator()();
    
    function_wrapper();

    function_wrapper(function_wrapper&& other);

    function_wrapper& operator=(function_wrapper&& other);

    function_wrapper(const function_wrapper&)=delete;
    function_wrapper(function_wrapper&)=delete;
    function_wrapper& operator=(const function_wrapper&)=delete;
};



function_wrapper.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "function_wrapper.hpp"
#include <iostream>

//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file


template<typename F>
function_wrapper::function_wrapper(F&& f):
    impl(new impl_type<F>(std::move(f)))
{}

void function_wrapper::operator()() { impl->call(); }
    
function_wrapper::function_wrapper() = default;

function_wrapper::function_wrapper(function_wrapper&& other):
        impl(std::move(other.impl))
    {}

function_wrapper& function_wrapper::operator=(function_wrapper&& other) {
    impl=std::move(other.impl);
    return *this;
}



threadsafe_queue.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef THREADSAFE_QUEUE_H
#define THREADSAFE_QUEUE_H
#endif

#include <memory>
#include <queue>
#include <mutex>
#include <memory>
#include <condition_variable>

//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file

template<typename T>

class threadsafe_queue {
private:
    mutable std::mutex mut;
    std::queue<T> data_queue;
    std::condition_variable data_cond;
public:
    threadsafe_queue();
    threadsafe_queue(threadsafe_queue const& other) {
        std::lock_guard<std::mutex> lk(other.mut);
        data_queue = other.data_queue;
    }
    threadsafe_queue& operator=(const threadsafe_queue&) = delete;
    void push(T new_value);
    bool try_pop(T& value);
    std::shared_ptr<T> try_pop();
    void wait_and_pop(T& value);
    std::shared_ptr<T> wait_and_pop();
    bool empty() const;
};



threadsafe_queue.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file

#include "threadsafe_queue.hpp"
#include "function_wrapper.hpp"

template<typename T>
void threadsafe_queue<T>::push(T new_value) {
    std::lock_guard<std::mutex> lk(threadsafe_queue::mut);
    data_queue.push(new_value);
    data_cond.notify_one();
}

template<typename T>
void threadsafe_queue<T>::wait_and_pop(T &value) {
    std::unique_lock<std::mutex> lk(mut);
    data_cond.wait(lk, [this] { return !data_queue.empty(); });
    value = data_queue.front();
    data_queue.pop();
}

template<typename T>
std::shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
    std::unique_lock<std::mutex> lk(mut);
    data_cond.wait(lk, [this] { return !data_queue.empty(); });
    std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
    data_queue.pop();
    return res;
}

template<typename T>
bool threadsafe_queue<T>::try_pop(T &value)
{
    std::lock_guard<std::mutex> lk(mut);
    if (data_queue.empty()) {
        return false;
    }
    value = data_queue.front();
    data_queue.pop();
    return true;
}

template<typename T>
std::shared_ptr<T> threadsafe_queue<T>::try_pop()
{
    std::lock_guard<std::mutex> lk(mut);
    if (data_queue.empty()){
        return std::shared_ptr<T>();
    }
    std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
    data_queue.pop();
    return res;
}

template<typename T>
bool threadsafe_queue<T>::empty() const
{
    std::lock_guard<std::mutex> lk(mut);
    return data_queue.empty();
}

template class threadsafe_queue<function_wrapper>;


helloworld.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <future>
#include <vector>
#include <iostream>
#include <unistd.h>

#include "function_wrapper.hpp"
#include "threadsafe_queue.hpp"


struct join_threads
{
    join_threads(std::vector<std::thread>&){}
};

class thread_pool
{
    std::atomic_bool done;
    threadsafe_queue<function_wrapper> work_queue;
    std::vector<std::thread> threads;
    join_threads joiner;

    void worker_thread()
    {
        while(!done)
        {
            function_wrapper task;
            if(work_queue.try_pop(task))
            {
                task();
            }
            else
            {
                std::this_thread::yield();
            }
        }
    }
public:
    template<typename FunctionType>
    std::future<typename std::result_of<FunctionType()>::type>
    submit(FunctionType f)
    {
        typedef typename std::result_of<FunctionType()>::type result_type;
        std::packaged_task<result_type()> task(std::move(f));
        std::future<result_type> res(task.get_future());
        work_queue.push(std::move(task));
        return res;
    }

    thread_pool():
        done(false),joiner(threads)
    {
        unsigned const thread_count=std::thread::hardware_concurrency();
        try
        {
            for(unsigned i=0;i<thread_count;++i)
            {
                threads.push_back(
                    std::thread(&thread_pool::worker_thread,this));
            }
        }
        catch(...)
        {
            done=true;
            throw;
        }
    }

    ~thread_pool()
    {
        done=true;
    }
};

void find_the_answer_to_ltuae(){
    std::cout << "About to sleep 1 second...";
    sleep(1);
    std::cout<<"The answer is " << 42 << std::endl;
}

int main()
{
    std::cout << "About to create my_threadpool...." << std::endl;
    thread_pool my_threadpool;
    std::cout << "Done creating my_threadpool...." << std::endl;
    std::cout << "Submitting first job now" << std::endl;
    my_threadpool.submit(find_the_answer_to_ltuae);
    sleep(10);
    std::cout <<"Finished" << std::endl;
}


Compilation result in next reply... I think I need to examine my delete members
if I comment out just one line from above

threadsafe_queue.cpp

 
//template class threadsafe_queue<function_wrapper>; 


we get a somewhat reasonable error..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(conan_starter) emcp@EMCPs-Mac-mini ch09_00_threadpool_simple % sh compile.sh
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Current conanbuildinfo.cmake directory: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
Scanning dependencies of target test_cpp_multi
[ 25%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/threadsafe_queue.cpp.o
[ 50%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/helloworld.cpp.o
[ 75%] Linking CXX executable bin/test_cpp_multi
Undefined symbols for architecture x86_64:
  "function_wrapper::function_wrapper<std::__1::packaged_task<void ()> >(std::__1::packaged_task<void ()>&&)", referenced from:
      std::__1::future<std::__1::result_of<void (* ())()>::type> thread_pool::submit<void (*)()>(void (*)()) in helloworld.cpp.o
  "threadsafe_queue<function_wrapper>::push(function_wrapper)", referenced from:
      std::__1::future<std::__1::result_of<void (* ())()>::type> thread_pool::submit<void (*)()>(void (*)()) in helloworld.cpp.o
  "threadsafe_queue<function_wrapper>::try_pop(function_wrapper&)", referenced from:
      thread_pool::worker_thread() in helloworld.cpp.o
  "threadsafe_queue<function_wrapper>::threadsafe_queue()", referenced from:
      thread_pool::thread_pool() in helloworld.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [bin/test_cpp_multi] Error 1
make[1]: *** [CMakeFiles/test_cpp_multi.dir/all] Error 2
make: *** [all] Error 2
(conan_starter) emcp@EMCPs-Mac-mini ch09_00_threadpool_simple % 


but when I uncomment it, thinking this is the fix... tons of errors come out..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
-- Conan: Adjusting output directories
-- Conan: Using cmake targets configuration
-- Conan: Adjusting default RPATHs Conan policies
-- Conan: Adjusting language standard
-- Current conanbuildinfo.cmake directory: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/build
Scanning dependencies of target test_cpp_multi
[ 25%] Building CXX object CMakeFiles/test_cpp_multi.dir/src/threadsafe_queue.cpp.o
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:38:11: error: overload resolution selected deleted operator '='
    value = data_queue.front();
    ~~~~~ ^ ~~~~~~~~~~~~~~~~~~
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:62:16: note: in instantiation of member function 'threadsafe_queue<function_wrapper>::try_pop' requested here
template class threadsafe_queue<function_wrapper>;
               ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:37:23: note: candidate function has been explicitly deleted
    function_wrapper& operator=(const function_wrapper&)=delete;
                      ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:33:23: note: candidate function not viable: no known conversion from 'std::__1::__deque_base<function_wrapper, std::__1::allocator<function_wrapper>
      >::value_type' (aka 'function_wrapper') to 'function_wrapper &&' for 1st argument
    function_wrapper& operator=(function_wrapper&& other);
                      ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:17:11: error: overload resolution selected deleted operator '='
    value = data_queue.front();
    ~~~~~ ^ ~~~~~~~~~~~~~~~~~~
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:62:16: note: in instantiation of member function 'threadsafe_queue<function_wrapper>::wait_and_pop' requested here
template class threadsafe_queue<function_wrapper>;
               ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:37:23: note: candidate function has been explicitly deleted
    function_wrapper& operator=(const function_wrapper&)=delete;
                      ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:33:23: note: candidate function not viable: no known conversion from 'std::__1::__deque_base<function_wrapper, std::__1::allocator<function_wrapper>
      >::value_type' (aka 'function_wrapper') to 'function_wrapper &&' for 1st argument
    function_wrapper& operator=(function_wrapper&& other);
                      ^
In file included from /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:3:
In file included from /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.hpp:6:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/queue:189:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:162:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__split_buffer:7:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:1701:19: error: overload resolution selected deleted operator '='
        *__result = *__first;
        ~~~~~~~~~ ^ ~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/algorithm:1735:23: note: in instantiation of function template specialization
      'std::__1::__copy_constexpr<const function_wrapper *, function_wrapper *>' requested here
        return _VSTD::__copy_constexpr(
                      ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:578:16: note: in instantiation of function template specialization 'std::__1::copy<const function_wrapper *, function_wrapper *>' requested
      here
        _VSTD::copy(__f, __m, __rb);
               ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:634:22: note: in instantiation of function template specialization 'std::__1::copy<const function_wrapper *, function_wrapper, function_wrapper
      *, function_wrapper &, function_wrapper **, long, 512>' requested here
        __r = _VSTD::copy(__fb, __fe, __r);
                     ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:1775:16: note: in instantiation of function template specialization 'std::__1::copy<function_wrapper, const function_wrapper *, const
      function_wrapper &, const function_wrapper *const *, long, 512, function_wrapper, function_wrapper *, function_wrapper &, function_wrapper **, long, 512>' requested here
        _VSTD::copy(__f, __m, __base::begin());
               ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/deque:1672:9: note: in instantiation of function template specialization 'std::__1::deque<function_wrapper, std::__1::allocator<function_wrapper>
      >::assign<std::__1::__deque_iterator<function_wrapper, const function_wrapper *, const function_wrapper &, const function_wrapper *const *, long, 512> >' requested here
        assign(__c.begin(), __c.end());
        ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/queue:236:43: note: in instantiation of member function 'std::__1::deque<function_wrapper, std::__1::allocator<function_wrapper> >::operator='
      requested here
    queue& operator=(const queue& __q) {c = __q.c; return *this;}
                                          ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.hpp:24:20: note: in instantiation of member function 'std::__1::queue<function_wrapper, std::__1::deque<function_wrapper,
      std::__1::allocator<function_wrapper> > >::operator=' requested here
        data_queue = other.data_queue;
                   ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:37:23: note: candidate function has been explicitly deleted
    function_wrapper& operator=(const function_wrapper&)=delete;
                      ^
/Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/function_wrapper.hpp:33:23: note: candidate function not viable: 1st argument ('const function_wrapper') would lose const qualifier
    function_wrapper& operator=(function_wrapper&& other);
                      ^
In file included from /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.cpp:3:
In file included from /Users/emcp/Dev/git/EMCP/cpp-multithreading-in-action/ch09_00_threadpool_simple/src/threadsafe_queue.hpp:6:
Topic archived. No new replies allowed.