質問

関数 _stack_push(stack * stk、void * el)へのポインターがあるとしましょう。 curry(_stack_push、my_stack)を呼び出して、 void * el だけを受け取る関数を取得できるようにしたいと思います。 Cではランタイム関数の定義が許可されていないため、それを行う方法を考えることはできませんでしたが、ここでは私よりもはるかに賢い人がいることを知っています:)。アイデアはありますか?

役に立ちましたか?

解決

C / C ++ / Objective-Cでのカレーについて説明しているLaurent Damiの論文を見つけました:

C / C ++ / Objective-cのカリー化された関数によるより多くの機能再利用性

Cでの実装方法に興味がある:

  

現在の実装では、既存のC構造を使用してカリー化メカニズムを追加しています。これは、コンパイラを変更するよりもはるかに簡単であり、カリー化の関心を証明するには十分です。ただし、このアプローチには2つの欠点があります。まず、カリー化された関数は型チェックできないため、エラーを回避するために慎重に使用する必要があります。第二に、カリー関数は引数のサイズを知ることができず、それらがすべて整数のサイズであるかのようにそれらをカウントします。

この論文には curry()の実装は含まれていませんが、関数ポインタおよびさまざまな関数

他のヒント

GCCは、ネストされた関数の定義の拡張機能を提供します。これはISO標準Cではありませんが、質問に非常に便利に答えることができるため、これは興味深いものです。つまり、ネストされた関数は親関数のローカル変数にアクセスでき、それらへのポインターは親関数からも返されます。

これは、一目瞭然の短い例です:

#include <stdio.h>

typedef int (*two_var_func) (int, int);
typedef int (*one_var_func) (int);

int add_int (int a, int b) {
    return a+b;
}

one_var_func partial (two_var_func f, int a) {
    int g (int b) {
        return f (a, b);
    }
    return g;
}

int main (void) {
    int a = 1;
    int b = 2;
    printf ("%d\n", add_int (a, b));
    printf ("%d\n", partial (add_int, a) (b));
}

ただし、この構造には制限があります。結果の関数へのポインターを保持する場合、

のように
one_var_func u = partial (add_int, a);

関数呼び出し u(0)は、 u が読み取る変数 a が< code> partial は終了しました。

GCCのドキュメントのこのセクションを参照してください。

これは私の頭の上の最初の推測です(最良の解決策ではないかもしれません)。

カレー関数は、ヒープからメモリを割り当て、そのヒープに割り当てられたメモリにパラメータ値を入れることができました。トリックは、返された関数が、ヒープに割り当てられたメモリからパラメーターを読み取ることになっていることを知ることです。返された関数のインスタンスが1つしかない場合、それらのパラメーターへのポインターをシングルトン/グローバルに格納できます。それ以外の場合、返された関数のインスタンスが複数ある場合、カレーは返された関数の各インスタンスをヒープに割り当てられたメモリ内に作成する必要があると思います(&quot;パラメータ」、「パラメータをプッシュ」、「ヒープに割り当てられたメモリに「他の関数を呼び出す」」)。その場合、割り当てられたメモリが実行可能かどうかに注意する必要があります。また、ウイルス対策プログラムを恐れる必要があるかもしれません(

)。

Cでカリー化する方法を次に示します。このサンプルアプリケーションでは、便宜上C ++ iostream出力を使用していますが、すべてCスタイルのコーディングです。

このアプローチの鍵は、 unsigned char の配列を含む struct を持つことです。この配列は、関数の引数リストを作成するために使用されます。呼び出される関数は、配列にプッシュされる引数の1つとして指定されます。結果の配列は、関数と引数のクロージャを実際に実行するプロキシ関数に渡されます。

この例では、クロージャーに引数をプッシュするいくつかのタイプ固有のヘルパー関数と、 struct またはその他をプッシュする汎用の pushMem()関数を提供しますメモリ領域。

このアプローチでは、クロージャーデータに使用されるメモリ領域の割り当てが必要です。メモリ管理が問題にならないように、このメモリ領域にスタックを使用することが最善です。また、必要な引数のための十分なスペースがあるが、メモリまたはスタック上の余分なスペースが未使用スペースによって占有されるほど大きくないように、クロージャーストレージメモリエリアをどのくらい大きくするかという問題もあります。

クロージャーデータを格納するために使用される配列の現在使用されているサイズの追加フィールドを含む、わずかに異なる定義のクロージャー構造の使用を実験しました。この異なるクロージャー構造体は、ヘルパー関数のユーザーがクロージャー構造体に引数を追加するときに独自の unsigned char * ポインターを維持する必要性をなくす修正されたヘルパー関数で使用されます。

注意事項と注意事項

次のサンプルプログラムは、Visual Studio 2013でコンパイルおよびテストされています。このサンプルの出力を以下に示します。この例でのGCCまたはCLANGの使用についてはわかりません。また、テストが32ビットアプリケーションで行われたと思われるため、64ビットコンパイラで発生する可能性のある問題についても確信がありません。また、このアプローチは、呼び出し元が( __ stdcall __ cdecl を返した後にスタックから引数をポップすることを処理する標準C宣言を使用する関数でのみ機能するようです。 code> Windows API)。

実行時に引数リストを作成してからプロキシ関数を呼び出すため、このアプローチではコンパイラが引数のチェックを実行できません。これは、コンパイラーがフラグを立てることができない不一致のパラメータータイプによる不可解な失敗につながる可能性があります。

サンプルアプリケーション

// currytest.cpp : Defines the entry point for the console application.
//
// while this is C++ usng the standard C++ I/O it is written in
// a C style so as to demonstrate use of currying with C.
//
// this example shows implementing a closure with C function pointers
// along with arguments of various kinds. the closure is then used
// to provide a saved state which is used with other functions.

#include "stdafx.h"
#include <iostream>

// notation is used in the following defines
//   - tname is used to represent type name for a type
//   - cname is used to represent the closure type name that was defined
//   - fname is used to represent the function name

#define CLOSURE_MEM(tname,size) \
    typedef struct { \
        union { \
            void *p; \
            unsigned char args[size + sizeof(void *)]; \
        }; \
    } tname;

#define CLOSURE_ARGS(x,cname) *(cname *)(((x).args) + sizeof(void *))
#define CLOSURE_FTYPE(tname,m) ((tname((*)(...)))(m).p)

// define a call function that calls specified function, fname,
// that returns a value of type tname using the specified closure
// type of cname.
#define CLOSURE_FUNC(fname, tname, cname) \
    tname fname (cname m) \
    { \
        return ((tname((*)(...)))m.p)(CLOSURE_ARGS(m,cname)); \
    }

// helper functions that are used to build the closure.
unsigned char * pushPtr(unsigned char *pDest, void *ptr) {
    *(void * *)pDest = ptr;
    return pDest + sizeof(void *);
}

unsigned char * pushInt(unsigned char *pDest, int i) {
    *(int *)pDest = i;
    return pDest + sizeof(int);
}

unsigned char * pushFloat(unsigned char *pDest, float f) {
    *(float *)pDest = f;
    return pDest + sizeof(float);
}

unsigned char * pushMem(unsigned char *pDest, void *p, size_t nBytes) {
    memcpy(pDest, p, nBytes);
    return pDest + nBytes;
}


// test functions that show they are called and have arguments.
int func1(int i, int j) {
    std::cout << " func1 " << i << " " << j;
    return i + 2;
}

int func2(int i) {
    std::cout << " func2 " << i;
    return i + 3;
}

float func3(float f) {
    std::cout << " func3 " << f;
    return f + 2.0;
}

float func4(float f) {
    std::cout << " func4 " << f;
    return f + 3.0;
}

typedef struct {
    int i;
    char *xc;
} XStruct;

int func21(XStruct m) {
    std::cout << " fun21 " << m.i << " " << m.xc << ";";
    return m.i + 10;
}

int func22(XStruct *m) {
    std::cout << " fun22 " << m->i << " " << m->xc << ";";
    return m->i + 10;
}

void func33(int i, int j) {
    std::cout << " func33 " << i << " " << j;
}

// define my closure memory type along with the function(s) using it.

CLOSURE_MEM(XClosure2, 256)           // closure memory
CLOSURE_FUNC(doit, int, XClosure2)    // closure execution for return int
CLOSURE_FUNC(doitf, float, XClosure2) // closure execution for return float
CLOSURE_FUNC(doitv, void, XClosure2)  // closure execution for void

// a function that accepts a closure, adds additional arguments and
// then calls the function that is saved as part of the closure.
int doitargs(XClosure2 *m, unsigned char *x, int a1, int a2) {
    x = pushInt(x, a1);
    x = pushInt(x, a2);
    return CLOSURE_FTYPE(int, *m)(CLOSURE_ARGS(*m, XClosure2));
}

int _tmain(int argc, _TCHAR* argv[])
{
    int k = func2(func1(3, 23));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    XClosure2 myClosure;
    unsigned char *x;

    x = myClosure.args;
    x = pushPtr(x, func1);
    x = pushInt(x, 4);
    x = pushInt(x, 20);
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func1);
    x = pushInt(x, 4);
    pushInt(x, 24);               // call with second arg 24
    k = func2(doit(myClosure));   // first call with closure
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;
    pushInt(x, 14);              // call with second arg now 14 not 24
    k = func2(doit(myClosure));  // second call with closure, different value
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    k = func2(doitargs(&myClosure, x, 16, 0));  // second call with closure, different value
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    // further explorations of other argument types

    XStruct xs;

    xs.i = 8;
    xs.xc = "take 1";
    x = myClosure.args;
    x = pushPtr(x, func21);
    x = pushMem(x, &xs, sizeof(xs));
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    xs.i = 11;
    xs.xc = "take 2";
    x = myClosure.args;
    x = pushPtr(x, func22);
    x = pushPtr(x, &xs);
    k = func2(doit(myClosure));
    std::cout << " main (" << __LINE__ << ") " << k << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func3);
    x = pushFloat(x, 4.0);

    float dof = func4(doitf(myClosure));
    std::cout << " main (" << __LINE__ << ") " << dof << std::endl;

    x = myClosure.args;
    x = pushPtr(x, func33);
    x = pushInt(x, 6);
    x = pushInt(x, 26);
    doitv(myClosure);
    std::cout << " main (" << __LINE__ << ") " << std::endl;

    return 0;
}

テスト出力

このサンプルプログラムからの出力。括弧内の数字は、関数呼び出しが行われるメインの行番号です。

 func1 3 23 func2 5 main (118) 8
 func1 4 20 func2 6 main (128) 9
 func1 4 24 func2 6 main (135) 9
 func1 4 14 func2 6 main (138) 9
 func1 4 16 func2 6 main (141) 9
 fun21 8 take 1; func2 18 main (153) 21
 fun22 11 take 2; func2 21 main (161) 24
 func3 4 func4 6 main (168) 9
 func33 6 26 main (175)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top