2

CPSフォームでプロシージャを定義しようとする次のコードがあります。C++の上位関数からラムダが返されたときのセグメンテーションエラー

#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; 
} 

var6行のコメントを外すと、コンパイラは予期したとおりにエラーをスローします。しかし、var7またはvar8の呼び出しがコメント解除されている場合、コードはコンパイルをパスしますが、実行時にランダムなセグメンテーションフォルトまたはバスエラーが発生します。コードはラムダを構築する間は安全ですが、ラムダを適用するとクラッシュします。

私の欠陥を指摘してくれてありがとう。


私がvar8よう

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

にコードを変更した場合、それがうまく動作することを補完することが有用です。


私は驚いたことにランダムに試してみました。私はちょうど最初の宣言を保持し、最初のものをコピーして2番目のものを変更します:

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); 
    }); 
    }; 
} 

だから何?

+0

「auto proc2s」を宣言して使用していない理由は何ですか? –

+0

申し訳ありませんが、それはちょうどデバッグスタッフでした。編集されました。 – Cowsay

答えて

2

パイプの実装に未定義の動作があります。参照によってすべてを取得します。パイプ関数のパラメータもパイプ関数が終了した後に破棄されます。コピー[=]でキャプチャするか、参照によってパイプに渡します。

+0

'proc2'と' pass'を呼び出すラムダの構築が遅れているので、 'proc1'だけが参照によって捕捉できることが分かりました。私の思考をGCを持つ言語から変えるのは難しいです。どうもありがとう。 – Cowsay

+0

ちなみに、試した後、ラムダのすべてのレベルですべてを参照として取得できるCPSバージョンの 'pipe'を書く方法を解明しました。かなり楽しい! – Cowsay

関連する問題