Cowsay
Cowsay

Reputation: 71

Segmentation fault when calling lambda returns from high-order function in C++11

I have the following code which trying to define procedures in CPS form.

#include <iostream>
#include <utility>
#include <type_traits>
#include <string>

using namespace std;


template <typename T> using Func = std::function<T>;
template <typename T> using Cont = Func<void (T &&)>;
template <typename T, typename U> using Proc = Func<void (T &&, Cont<U>)>;

template <typename T> struct RevProc;
template <typename T, typename U> struct RevProc<Proc<T, U>> {
  using ArgType = T;
  using RetType = U;
};

template <typename P1, typename P2> auto pipe(P1 proc1, P2 proc2)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<P2>::RetType> {
  using T = typename RevProc<P1>::ArgType;
  using U = typename RevProc<P2>::ArgType;
  static_assert(is_same<U, typename RevProc<P1>::RetType>::value,
    "Mismatch procedure type.");
  using V = typename RevProc<P2>::RetType;
  return [&] (T &&t, Cont<V> pass) {
    proc1(move(t), [&] (U &&u) {
      proc2(move(u), pass);
    });
  };
}
template <typename P1, typename P2, typename... Ps> auto pipe(P1 proc1, P2 proc2, Ps ...procs)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<decltype(pipe(proc2, procs...))>::RetType> {
  auto proc2s = pipe(proc2, procs...);
  return pipe(proc1, proc2s);
}

int main() {
  Func<void ()> var1;
  Cont<int> var2([] (int &&x) {});
  Proc<int, int> var3([] (int &&x, Cont<int> pass) {
    pass(x + 1);
  });
  auto var4 = pipe(var3, var3);
  var4(42, [] (int &&x) {
    cout << x << endl;
  });
  Proc<string, int> var5([] (string &&str, Cont<int> pass) {
    pass(str.length());
  });
  // auto var6 = pipe(var5, var5);
  Proc<int, int> var7 = pipe(var3, var3, var3);
  // var7(42, [] (int &&x) {
  //   cout << x << endl;
  // });
  auto var8 = pipe(var5, var3, var3);
  var8("something", [] (int &&x) {
    cout << x << endl;
  });

  return 0;
}

If I uncomment var6 line, the compiler throw an error as expect. However, if either var7 or var8's calling is uncommented, then the code passes compilation but a random segmentation fault or bus error would be triggered at runtime. The code is safe during building the lambda but crashing when applying them.

Thanks for pointing out my faulty.


It may be useful to complement that it runs well if I modify the code to

var7 = pipe(var3, pipe(var3, var3));

So as var8.


I tried randomly and it fixed, to my surprise. I just keep the first declaration and modify the second by copying the first:

template <typename P1, typename P2, typename... Ps> constexpr auto pipe(P1 proc1, P2 proc2, Ps ...procs)
  -> Proc<typename RevProc<P1>::ArgType, typename RevProc<decltype(pipe(proc2, procs...))>::RetType> {
  using T = typename RevProc<P1>::ArgType;
  using U = typename RevProc<P2>::ArgType;
  static_assert(is_same<U, typename RevProc<P1>::RetType>::value,
    "Mismatch procedure type.");
  using V = typename RevProc<P2>::RetType;
  return [&] (T &&t, Cont<V> pass) {
    proc1(move(t), [&] (U &&u) {
      pipe(proc2, procs...)(move(u), pass);
    });
  };
}

So, what the hell?

Upvotes: 2

Views: 552

Answers (1)

Mateusz
Mateusz

Reputation: 141

Your pipe implementation has an undefined behavior. It captures everything by reference, so also pipe's function parameters, which are destroyed after pipe function ends. Either capture by copy [=], or pass to pipe by reference.

Upvotes: 2

Related Questions