I am going to give a presentation on lambda expressions for our local C/C++ Meetup, so I am preparing several examples to demonstrate how lambda expressions can solve certain tasks.
One of the examples is to create a qsort function that takes a lambda expression for the comparison function. It works fine for the simple case of sort_ascending() below, but for sort_descending(), where I include a counter integer that is captured by reference and incremented with each call to the lambda expression, I get a segfault at the first attempt to increment the counter.
I've looked at the lambda expression documentation I could find, especially cppreference.cpp. As far as I can tell, there's no reason why testcount in sort_descending should be out-of-scope: testcount is in the same stack frame as the call to qsort_l, and the lambda expression that captured testcount is no longer used when qsort_l returns.
// -*- compile-command: "g++ -std=c++11 -ggdb -Wno-pmf-conversions -Wall -Werror -Weffc++ -pedantic -o minfail minfail.cpp" -*-
#include <stdlib.h>
#include <stdio.h>
void print_ints(const int* arr, int count)
{
for (int i=0; i<count; ++i)
{
if (i)
putchar(' ');
printf("%3d", arr[i]);
}
putchar('\n');
}
class Compar_Base
{
public:
virtual ~Compar_Base() { }
virtual int comp(const void* left, const void* right) const = 0;
static int compare(const void* left, const void* right, void* obj)
{
return static_cast<Compar_Base*>(obj)->comp(left,right);
}
};
template <typename Func>
class Compar_Concrete : public Compar_Base
{
Func &m_f;
public:
Compar_Concrete(Func f) : m_f(f) { }
virtual ~Compar_Concrete() { }
Compar_Concrete(const Compar_Concrete&) = delete;
Compar_Concrete& operator=(const Compar_Concrete&) = delete;
virtual int comp(const void* left, const void* right) const
{
return m_f(left,right);
}
};
/** Shiny, new qsort for lambda expressions. */
template <typename Func>
void qsort_l(void *base, size_t member_count, size_t member_size, Func f)
{
Compar_Concrete<Func> comp(f);
qsort_r(base, member_count, member_size, Compar_Base::compare, &comp);
}
void sort_ascending(int* intlist, int count)
{
auto f = [](const void* left, const void* right) -> int
{
return *static_cast<const int*>(left) - *static_cast<const int*>(right);
};
qsort_l(intlist, count, sizeof(int), f);
}
void sort_descending(int* intlist, int count)
{
int testcount = 0;
auto f = [&testcount](const void* left, const void* right) -> int
{
// Segmentation fault at this line:
++testcount;
return *static_cast<const int*>(right) - *static_cast<const int*>(left);
};
qsort_l(intlist, count, sizeof(int), f);
printf("\nIt took %d tests to complete the sort.\n", testcount);
}
int main(int argc, char** argv)
{
int ilist[] = {1,9,2,8,3,7,4,6,5};
int count = sizeof(ilist) / sizeof(int);
print_ints(ilist,count);
sort_ascending(ilist, count);
print_ints(ilist,count);
sort_descending(ilist, count);
print_ints(ilist,count);
}
I compiled the code above using g++ versions 4.8.4 and 5.4.0 with the same result (ie a segfault). You can see the compiler options in use by looking at the compile-command variable declared at the top of the code listing.
Aucun commentaire:
Enregistrer un commentaire