5

I did a test with c++ parallel quicksort program as below first with list as container then I moved to a generic container type, but it reported the captioned error.

Can help with this?

#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for
#include <list>
#include <algorithm>
#include <type_traits>
#include <iterator>

template<typename F, typename A>
static std::future<typename std::result_of<F(A&&)>::type> spawn_task(F&& f, A&& a)
{
    typedef typename std::result_of<F(A&&)>::type result_type;

    std::packaged_task<result_type(A&&)> task(std::move(f));
    std::future<result_type> res(task.get_future());
    std::thread myThread(std::move(task), std::move(a));
    myThread.detach();
    return res;
}

template<class T, template<class T> class Container>

static Container<T> parallel_quick_sort(Container<T> input)
{
    if (input.empty())
    {
        return input;
    }

    Container<T> result;
    result.splice(result.begin(), input, input.begin());
    T const& partition_val = *result.begin();

    typename Container<T>::iterator divide_point = std::partition
    (input.begin(), input.end(), [&](T const& t)
        {
         return t<partition_val;
        }
    );

    Container<T> lower_part;
    lower_part.splice(lower_part.end(), input, input.begin(), divide_point);

    std::future<Container<T> > new_lower
    (
        spawn_task(&parallel_quick_sort<T>, std::move(lower_part))
    );
    Container<T> new_higher(parallel_quick_sort(std::move(input)));
    result.splice(result.end(), new_higher);
    result.splice(result.begin(), new_lower.get());

    return result;
}


static void testQuickSort()
{
    std::list<int> toSort={1, 4, 3, 6, 4, 89, 3};
    std::for_each
    (    
        std::begin(toSort), std::end(toSort), [](int n)
        {
            std::cout << n << std::endl;
        }
    );

    std::list<int> sorted;
    sorted = parallel_quick_sort(toSort);
    std::for_each
    (
        std::begin(sorted), std::end(sorted), [](int n)
        {
            std::cout << n << std::endl;
        }
    );
}

The error message is:

../src/TestGenericQuickSort.h: In static member function ‘static void TestGenericQuickSort::testQuickSort()’:

../src/TestGenericQuickSort.h:67:41: error: no matching function for call to ‘TestGenericQuickSort::parallel_quick_sort(std::list&)’ sorted=parallel_quick_sort(toSort);

../src/TestGenericQuickSort.h:67:41: note: candidate is:

../src/TestGenericQuickSort.h:33:22: note: template class Container> static Container TestGenericQuickSort::parallel_quick_sort(Container) static Container parallel_quick_sort(Container input)

../src/TestGenericQuickSort.h:33:22: note: template argument deduction/substitution failed:

../src/TestGenericQuickSort.h:67:41: error: wrong number of template arguments (2, should be 1) sorted=parallel_quick_sort(toSort);

../src/TestGenericQuickSort.h:32:44: error: provided for ‘template class Container’ template class Container>

Kamiccolo
  • 7,758
  • 3
  • 34
  • 47
Michael
  • 673
  • 2
  • 5
  • 23
  • The std::list template has two type parameters, not one. See http://stackoverflow.com/questions/5301706/default-values-in-templates-with-template-arguments-c – heinrichj Feb 28 '14 at 14:07

2 Answers2

5

You pass a std::list, whose full declaration is

template <typename T, typename Alloc = std::allocator<T> >
class list;

So, it has 2 template parameters, though the seconds one has a default value (and that's the reason why you don't see it).

A better design would be to pass 2 input iterators and an output iterator to your function:

template <typename IteratorIn, typename IteratorOut>
static IteratorOut parallel_quick_sort(IteratorIn begin, IteratorIn end, IteratorOut out);

See std::sort for more details on this signature.

lisyarus
  • 15,025
  • 3
  • 43
  • 68
  • 1
    Beware, `InputIterator` implies that once you consume an element from it, you cannot obtain it again. It is not suitable as an iterator for a sort function. Think of reading from a tape in one direction only. You want to say `ForwardIterator` for clarity. – mockinterface Feb 28 '14 at 14:21
  • @mockinterface: in my code `InputIterator` does not name iterator category, just it's semantics. Consideing the quick sort function, maybe a `RandomAccessIterator` would be better here? – lisyarus Feb 28 '14 at 14:31
  • 1
    Yes I of course understand that you didn't name it as an iterator category, but it's better to be strict about this, because `InputIterator` has a concrete meaning in the standard. Re *Forward* vs *Random* - it depends on the underlying requirements of the sort implementation, but either is better than *Input*. Consider `IteratorIn / IteratorOut`. – mockinterface Feb 28 '14 at 14:37
  • @mockinterface: I agree, `InputIterator` can make the signature confusing in cases like that. – lisyarus Feb 28 '14 at 14:39
0

First I need to thank lisyarus for the answer, without it I couldn't work things out as below.

But since this is an exercise about the inner workings of C++, I tried to modify my own code to make it work and finally it worked as below.

#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for
#include <list>
#include <algorithm>
#include <type_traits>
#include <iterator>

template<typename F, typename A>
static std::future<typename std::result_of<F(A&&)>::type> spawn_task(F&& f, A&& a)
{
    typedef typename std::result_of<F(A&&)>::type result_type;
    std::packaged_task<result_type(A&&)> task(std::move(f));
    std::future<result_type> res(task.get_future());
    std::thread myThread(std::move(task), std::move(a));
    myThread.detach();
    return res;
}
template<class T, class Alloc, template<class T, class Alloc> class Container>
static Container<T, Alloc> parallel_quick_sort(Container<T, Alloc> input)
{
    if (input.empty())
    {
        return input;
    }

    Container<T, Alloc> result;
    result.splice(result.begin(), input, input.begin());
    T const& partition_val = *result.begin();
    typename Container<T, Alloc>::iterator divide_point = std::partition(
            input.begin(), input.end(), [&](T const& t)
            {   return t<partition_val;});
    Container<T, Alloc> lower_part;
    lower_part.splice(lower_part.end(), input, input.begin(), divide_point);


    std::future<Container<T, Alloc> > new_lower(
            spawn_task(&parallel_quick_sort<T, Alloc, Container>, std::move(lower_part)));

    Container<T, Alloc> new_higher(parallel_quick_sort(std::move(input)));
    result.splice(result.end(), new_higher);
    result.splice(result.begin(), new_lower.get());
    return result;

}



static void testQuickSort()
{
    std::list<int> toSort={1,4,3,6,4,89,3};
    std::for_each(std::begin(toSort), std::end(toSort), [](int n){ std::cout << n << std::endl;});
    std::list<int> sorted;
    sorted=parallel_quick_sort(toSort);
    std::for_each(std::begin(sorted), std::end(sorted), [](int n){ std::cout << n << std::endl;});


}

Thank you for all your help!!!

Michael
  • 673
  • 2
  • 5
  • 23
  • I tried with vector then failed because vector doesn't have the splice function, so this is not a generic function now. Will work to make one later... – Michael Feb 28 '14 at 15:04