c++ - Why does accessing my capture-by-reference variable cause a segfault in my lambda function? -


i going give presentation on lambda expressions our local c/c++ meetup, preparing several examples demonstrate how lambda expressions can solve tasks.

one of examples create qsort function takes lambda expression comparison function. works fine simple case of sort_ascending() below, sort_descending(), include counter integer captured reference , incremented each call lambda expression, segfault @ first attempt increment counter.

i've looked @ lambda expression documentation find, cppreference.cpp. far can tell, there's no reason why testcount in sort_descending should out-of-scope: testcount in same stack frame call qsort_l, , lambda expression captured testcount 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) {    (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 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 @ 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 complete 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 code above using g++ versions 4.8.4 , 5.4.0 same result (ie segfault). can see compiler options in use looking @ compile-command variable declared @ top of code listing.

func &m_f; 

this class member, reference. here's constructor:

compar_concrete(func f) : m_f(f) { } 

the m_f member initialized reference constructor's f parameter.

unfortunately, parameter gets passed value. when constructor ends , terminates, f goes out of scope , gets destroyed, leaving dangling reference in class member.

subsequent usage of m_f ends dereferencing invalid reference, , results in undefined behavior. that's @ least 1 obvious bug.

the entire code full of type-unsafe generic void * hiding few other bugs well, there's no point in looking more bugs, when pretty show-stopper.


Comments

Popular posts from this blog

What is happening when Matlab is starting a "parallel pool"? -

angular - DownloadURL return null in below code -

php - Cannot override Laravel Spark authentication with own implementation -