Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

<function-style-cast> error: Cannot convert from 'initializer list' to 'std::thread'

I am trying to parallelize quicksort using std::threads, and I am receiving an error I am unfamiliar with since I just started multithreading. The error is probably so simple, I keep skipping over it. Can someone please shed some light on the problem. Here is the code and the only error that appears:

        #define _CRT_SECURE_NO_WARNINGS

#include <iostream> //cout, endl
#include <cstdlib>  //srand
#include <algorithm>//copy, random_shuffle
#include <iterator> //ostream_iterator
#include "ratio.h"
#include <vector>
#include <iostream>
#include <thread>
#include "quicksort.h"
#include "sort_small_arrays.h"

template< typename T>
unsigned partition(T* a, unsigned begin, unsigned end) {
    unsigned i = begin, last = end - 1;
    T pivot = a[last];

    for (unsigned j = begin; j<last; ++j) {
        if (a[j]<pivot) {
            std::swap(a[j], a[i]);
            ++i;
        }
    }
    std::swap(a[i], a[last]);
    return i;
}

/* iterative */
#define STACK
#define xVECTOR
#define xPRIORITY_QUEUE 

#include <utility> // std::pair

template <typename T>
using triple = typename std::pair< T*, std::pair<unsigned, unsigned>>;

template< typename T>
struct compare_triples {
    bool operator() (triple<T> const& op1, triple<T> const& op2) const {
        return op1.second.first > op2.second.first;
    }
};

#ifdef STACK
#include <stack>
template< typename T>
using Container = std::stack< triple<T>>;
#define PUSH push
#define TOP  top
#define POP  pop
#endif

#ifdef VECTOR
#include <vector>
template< typename T>
using Container = std::vector< triple<T>>;
#define PUSH push_back
#define TOP  back
#define POP  pop_back
#endif

#ifdef PRIORITY_QUEUE
#include <queue>
template< typename T>
using Container = std::priority_queue< triple<T>, std::vector<triple<T>>, compare_triples<T> >;
#define PUSH push
#define TOP  top
#define POP  pop
#endif

//Thread quicksorts a single range of elements and decrements thread counter at the end
template< typename T>
void threadsort_iterative_aux(Container<T> & ranges, int &currentThreads)
{
    triple<T> r = ranges.TOP();
    ranges.POP();

    T*       a = r.first;
    unsigned b = r.second.first;
    unsigned e = r.second.second;

    //base case
    if (e - b<6) {
        switch (e - b) {
        case 5: quicksort_base_5(a + b); break;
        case 4: quicksort_base_4(a + b); break;
        case 3: quicksort_base_3(a + b); break;
        case 2: quicksort_base_2(a + b); break;
        }
        continue;
    }

    unsigned q = partition(a, b, e);

    ranges.PUSH(std::make_pair(a, std::make_pair(b, q)));
    ranges.PUSH(std::make_pair(a, std::make_pair(q + 1, e)));
    --currentThreads;
}

template< typename T>
void quicksort(T* a, unsigned begin, unsigned end, int num_threads)
{
    //Number of threads currently running other than the main thread
    int currentThreads = 0;

    //Ranges of elements to sort
    Container<T> ranges;
    ranges.PUSH(std::make_pair(a, std::make_pair(begin, end)));

    //Dynamic vector of threads
    std::vector<std::thread> threads;

    //Loops till all threads are done AND nothing left to sort
    while (!ranges.empty() || currentThreads != 0)
    {
        //Doesn't bother doing anything if the range is empty but other threads are still running
        if (!ranges.empty())
        {
            //If we can make more threads, make a thread and give it the top range to sort
            if (currentThreads < num_threads)
            {
                ++currentThreads;
                threads.push_back(std::thread(threadsort_iterative_aux, std::ref(ranges), std::ref(currentThreads)));

            }
            //Starts sorting itself if maximum number of threads are running
            else
            {
                triple<T> r = ranges.TOP();
                ranges.POP();
                T*       a = r.first;
                unsigned b = r.second.first;
                unsigned e = r.second.second;

                //Optimized sorting of a range between 2 and 5 elements
                if (e - b < 6) {
                    switch (e - b) {
                    case 5: quicksort_base_5(a + b); break;
                    case 4: quicksort_base_4(a + b); break;
                    case 3: quicksort_base_3(a + b); break;
                    case 2: quicksort_base_2(a + b); break;
                    }
                    continue;
                }

                unsigned q = partition(a, b, e);

                ranges.PUSH(std::make_pair(a, std::make_pair(b, q)));
                ranges.PUSH(std::make_pair(a, std::make_pair(q + 1, e)));
            }
        }
    }
}

template< typename T>
bool check_is_sorted(T* a, unsigned size)
{
    for (unsigned int i = 1; i<size; ++i) {
        if (!(a[i - 1] <= a[i])) {
            return false;
        }
    }
    return true;
}

bool test_int(unsigned size, unsigned num_threads) {
    int* a = new int[size];
    for (unsigned i = 0; i<size; ++i) { a[i] = i; }
    std::srand(static_cast<unsigned int>(std::time(NULL)));
    std::random_shuffle(a, a + size);
    quicksort(a, 0, size, num_threads);
    bool retval = check_is_sorted(a, size);
    delete[] a;
    return retval;
}

void test0() {
    if (test_int(200, 1)) { std::cout << "OK\n"; }
    else { std::cout << "Failed\n"; }
}

#include <cstdio>    /* sscanf */
int main(int argc, char ** argv) 
{
    test0();
    return 0;
}

Severity Code Description Project File Line Error C2440 '': cannot convert from 'initializer list' to 'std::thread' Line 145 (contains:

threads.push_back(std::thread(threadsort_iterative_aux, std::ref(ranges), std::ref(currentThreads)));)

like image 479
Amro the Student Avatar asked Jul 20 '16 08:07

Amro the Student


1 Answers

You pass a triple<T> as the first parameter to threadsort_iterative_aux when creating the thread, but the function expects a Container<T> & there.

Also note that passing parameters by non-const-reference with this signature, you need to wrap the parameters in std::ref() at the caller's side.

This is basically the same behavior as with std::bind: If you omit the std::ref, the compiler will copy the value into the binding and then pass that copy as a parameter into the function call upon invocation. Since the copy is immutable though, that will break with non-const references. Which is a good thing, as it protects you from accidentally losing the side-effects on that parameter.

Last but not least, the bind mechanism breaks template-argument deduction. Since you are not actually calling the function at the point where you create the thread, the compiler is unable to deduce the template parameter for the function from the arguments automatically. You have to give the parameter explicitly:

threads.push_back(std::thread(threadsort_iterative_aux<T>, std::ref(ranges), std::ref(currentThreads)));
//                               note the <T> here  ---^

Since these are a mighty bunch of bind-headaches to worry about, you might want to just use a lambda instead, which suffers from none of those issues:

threads.push_back(std::thread([&]() { threadsort_iterative_aux(ranges, currentThreads); }));
like image 119
ComicSansMS Avatar answered Nov 14 '22 11:11

ComicSansMS