Trouble with pointer to templated class method

Viewed 41

I was writing code for the following parallel processing task:

  1. A std::vector<T> contains data items that need to be processed
  2. A function process_data<T&> does that processing on such a single data item

In my software I want to do this for different types T, so I wrote a template class:

#include <mutex>
#include <thread>
#include <vector>

// Parallel processing class.

template <class T>
class parallel_processing {
public:

    // Do parallel processing for all items in the vector.

    void do_parallel_processing(std::vector<T>& items,
                                void (*item_processor)(T&),
                                size_t thread_count = 1)
    {
        // Check if we should do sequential processing after all.

        if (thread_count <= 1) {
            for (size_t i = 0; i < items.size(); i++)
                item_processor(items[i]);
            return;
        }

        // Proceed with parallel processing.

        item_processor_ptr = item_processor;
        items_ptr          = &items;
        next_item_index    = 0;

        // Spawn all threads.

        std::vector<std::thread> threads;
        for (size_t i = 0; i < thread_count; i++)
            threads.push_back(std::thread(item_thread_worker));

        // The current thread should also work hard. This has an advantage: calling join() 
        // (see below) blocks the thread, costing time. Method 'item_thread_worker' however
        // only returns if all items are processed and thus all threads must also have 
        // finished (or are busy with their last item...).

        item_thread_worker();

        // Wait for all threads to finish and call join on them.

        for (auto& this_thread : threads)
            this_thread.join();
    }
private:

    // Get the next index to process.

    int get_next_item_index()
    {
        const std::lock_guard<std::mutex> lock(next_item_index_mutex);

        // Check if we're already done.

        if (next_item_index >= (int)items_ptr->size())
            return -1;

        // Next index (first return, then increment).

        return next_item_index++;
    }

    // Thread-worker method for items.

    void item_thread_worker()
    {
        int item_index;

        // Keep on processing while not all items are processed.

        while ((item_index = get_next_item_index()) >= 0)
            item_processor_ptr((*items_ptr)[item_index]);
    }

    // Properties.

    std::mutex next_item_index_mutex;     // For thread-safe access to 'next_item_index'.
    int next_item_index;                  // Identifies the next item index to process.
    void (*item_processor_ptr)(T& items); // The item processing function.
    std::vector<T>* items_ptr;            // Pointer to the vector with items to be processed.
};

The idea is simple and worked when it was not yet in a template class but separate functions but then of course could only be coded for a single type T:

  1. A number of threads is started and they all run the same worker method
  2. The workers pick a data item to be processed from the std::vector<T>, and call the function to process the selected item until all items are processed

The compiler (VS2019) complains about the line:

threads.push_back(std::thread(item_thread_worker));

'use & to create a pointer to a member'

So I tried threads.push_back(std::thread(&item_thread_worker)); which gives me the error:

''&': illegal operation on bound member function expression'

So I tried all kind of things: with (), with the class in front &parallel_processing<T>:: or &parallel_processing:: but all I get are different errors...

My knowledge about C++ is clearly not enough to solve this, help is appreciated.

1 Answers

As item_thread_worker is a non-static member function, it needs a object to be called with.

When you create your threads, you don't specify any objects. Those objects (which becomes the this pointer inside the functions) are passed as a hidden "first" argument.

Another point is that to get a pointer to a member function, you must use the pointer-to operator &. Unlike non-member functions, member functions do not decay to pointers to themselves. And you need to use the full scope, with the class-name.

So to create a thread using a non-static member function, that should be called on this object, you need to do std::thread(&parallel_processing::item_thread_worker, this).

Related