有没有一种方法可以用C进行运算?

时间:2022-10-11 22:03:59

Say I have a pointer to a function _stack_push(stack* stk, void* el). I want to be able to call curry(_stack_push, my_stack) and get back a function that just takes void* el. I couldn't think of a way to do it, since C doesn't allow runtime function definition, but I know there are far cleverer people than me here :). Any ideas?

假设我有一个指向函数_stack_push(stack* stk, void* el)的指针。我希望能够调用curry(_stack_push, my_stack)并返回一个只接受void* el的函数。我想不出一种方法,因为C不允许运行时函数定义,但我知道这里有比我聪明得多的人:)。什么好主意吗?

4 个解决方案

#1


20  

I found a paper by Laurent Dami that discusses currying in C/C++/Objective-C:

我发现洛朗·达米的一篇论文,讨论了C/C++/Objective-C中的加仑数:

More Functional Reusability in C/C++/Objective-c with Curried Functions

在C/ c++ /Objective-c中使用Curried函数的更多功能的可重用性。

Of interest to how it is implemented in C:

感兴趣的是如何在C中执行:

Our current implementation uses existing C constructs to add the currying mechanism. This was much easier to do than modifying the compiler, and is sufficient to prove the interest of currying. This approach has two drawbacks, however. First, curried functions cannot be type-checked, and therefore require careful use in order to avoid errors. Second, the curry function cannot know the size of its arguments, and counts them as if they were all of the size of an integer.

我们当前的实现使用现有的C构造来添加并发机制。这比修改编译器要容易得多,并且足以证明对并发的兴趣。然而,这种方法有两个缺点。首先,绑定函数不能进行类型检查,因此需要谨慎使用,以避免错误。其次,curry函数不能知道参数的大小,并将它们计数为一个整数的大小。

The paper does not contain an implementation of curry(), but you can imagine how it is implemented using function pointers and variadic functions.

本文不包含curry()的实现,但是可以想象如何使用函数指针和变量函数实现它。

#2


6  

GCC provides an extension for the definition of nested functions. Although this is not ISO standard C, this may be of some interest, since it enables to answer the question quite conveniently. In short, nested function can access the parent function local variables and pointers to them can be returned by the parent function as well.

GCC为嵌套函数的定义提供了一个扩展。虽然这不是ISO标准C,但这可能是有些兴趣,因为它可以很方便地回答这个问题。简而言之,嵌套函数可以访问父函数局部变量,并且父函数也可以返回指向它们的指针。

Here is a short, self-explanatory example:

这里有一个简短的、自我解释的例子:

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

There is however a limitation to this construction. If you keep a pointer to the resulting function, as in

然而,这种结构有一个局限性。如果你有一个指向结果函数的指针,比如in

one_var_func u = partial (add_int, a);

the function call u(0) may result in an unexpected behaviour, as the variable a which u reads was destroyed just after partial terminated.

函数调用u(0)可能会导致意外行为,因为u读取的变量a在部分终止后被销毁。

See this section of GCC's documentation.

请参阅GCC文档的这一部分。

#3


4  

Here's my first guess off the top of my head (may not be the best solution).

这是我的第一个猜想(可能不是最好的解决方案)。

The curry function could allocate some memory off the heap, and put the parameter values into that heap-allocated memory. The trick is then for the returned function to know that it's supposed to read its parameters from that heap-allocated memory. If there's only one instance of the returned function, then a pointer to those parameters can be stored in a singleton/global. Otherwise if there's more than one instance of the returned function, then I think that curry needs to create each instance of the returned function in the heap-allocated memory (by writing opcodes like "get that pointer to the parameters", "push the parameters", and "invoke that other function" into the heap-allocated memory). In that case you need to beware whether allocated memory is executable, and maybe (I don't know) even be afraid of anti-virus programs.

curry函数可以从堆中分配一些内存,并将参数值放在堆分配的内存中。然后,对于返回的函数,要知道它应该从该堆分配的内存中读取它的参数。如果返回函数只有一个实例,那么指向这些参数的指针可以存储在单例/全局变量中。否则如果有多个实例返回的函数,然后我觉得咖喱需要创建的每个实例返回的函数在堆上分配内存(通过编写操作码“得到指针参数”,“把参数”,和“调用其他函数”堆上分配内存)。在这种情况下,您需要注意分配的内存是否可执行,甚至可能(我不知道)害怕杀毒程序。

#4


0  

Here is an approach to doing currying in C. While this sample application is using the C++ iostream output for convenience it is all C style coding.

这里有一种在C中执行同步的方法,尽管这个示例应用程序使用c++ iostream输出,但为了方便起见,它都是C样式的代码。

The key to this approach is to have a struct which contains an array of unsigned char and this array is used to build up an argument list for a function. The function to be called is specified as one of the arguments that are pushed into the array. The resulting array is then given to a proxy function which actually executes the closure of function and arguments.

这个方法的关键是要有一个包含无符号字符数组的结构,这个数组用来建立一个函数的参数列表。要调用的函数被指定为推入数组的参数之一。然后将结果数组提供给一个代理函数,该函数实际上执行函数和参数的闭包。

In this example I provide a couple of type specific helper functions to push arguments into the closure as well as a generic pushMem() function to push a struct or other memory region.

在本例中,我提供了几个类型特定的helper函数,用于将参数推入闭包,以及一个泛型pushMem()函数来推入结构体或其他内存区域。

This approach does require allocation of a memory area that is then used for the closure data. It would be best to use the stack for this memory area so that memory management does not become a problem. There is also the issue as to how large to make the closure storage memory area so that there is sufficient room for the necessary arguments but not so large that excess space in memory or on the stack is taken up by unused space.

这种方法确实需要分配一个内存区域,然后用于闭包数据。最好对这个内存区域使用堆栈,这样内存管理就不会成为问题。还有一个问题是,要让闭包存储内存区域有多大,以便有足够的空间容纳必要的参数,但又不能大到内存或堆栈中的多余空间被未使用的空间占用。

I have experimented with the use of a slightly differently defined closure struct which contains an additional field for the currently used size of the array used to store the closure data. This different closure struct is then used with a modified helper functions which removes the need for the user of the helper functions to maintain their own unsigned char * pointer when adding arguments to the closure struct.

我尝试过使用一个稍微不同定义的闭包结构,它包含一个用于存储闭包数据的数组当前使用的大小的附加字段。然后,将这个不同的闭包结构体与一个修改后的helper函数一起使用,它消除了在向闭包结构中添加参数时,helper函数的用户需要维护自己的无符号char *指针的需要。

Notes and caveats

笔记和警告

The following example program was compiled and tested with Visual Studio 2013. The output from this sample is provided below. I am not sure about the use of GCC or CLANG with this example nor am I sure as to issues that may be seen with a 64 bit compiler as I am under the impression that my testing was with a 32 bit application. Also this would approach would only seem to work with functions that use the standard C declaration in which the calling function handles popping the arguments from the stack after the callee returns (__cdecl and not __stdcall in Windows API).

下面的示例程序是用Visual Studio 2013编译和测试的。这个示例的输出如下所示。我不确定在这个示例中使用GCC或CLANG,也不确定在64位编译器中可能遇到的问题,因为我认为我的测试是在32位应用程序中进行的。此外,这种方法似乎只适用于使用标准C声明的函数,在该声明中,调用函数在被调用者返回(__cdecl,而不是Windows API中的__stdcall)后处理从堆栈中取出的参数。

Since we are building the argument list at run time and then calling a proxy function, this approach does not allow the compiler to perform a check on arguments. This could lead to mysterious failures due to mismatched parameter types which the compiler is unable to flag.

由于我们在运行时构建了参数列表,然后调用代理函数,这种方法不允许编译器对参数进行检查。这可能导致由于编译器无法标记的参数类型不匹配而导致的神秘故障。

Example application

示例应用程序

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

Test output

测试输出

Output from this sample program. The number in parenthesis is the line number in the main where the function call is made.

此示例程序的输出。括号中的数字是函数调用所在的主行号。

 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)

#1


20  

I found a paper by Laurent Dami that discusses currying in C/C++/Objective-C:

我发现洛朗·达米的一篇论文,讨论了C/C++/Objective-C中的加仑数:

More Functional Reusability in C/C++/Objective-c with Curried Functions

在C/ c++ /Objective-c中使用Curried函数的更多功能的可重用性。

Of interest to how it is implemented in C:

感兴趣的是如何在C中执行:

Our current implementation uses existing C constructs to add the currying mechanism. This was much easier to do than modifying the compiler, and is sufficient to prove the interest of currying. This approach has two drawbacks, however. First, curried functions cannot be type-checked, and therefore require careful use in order to avoid errors. Second, the curry function cannot know the size of its arguments, and counts them as if they were all of the size of an integer.

我们当前的实现使用现有的C构造来添加并发机制。这比修改编译器要容易得多,并且足以证明对并发的兴趣。然而,这种方法有两个缺点。首先,绑定函数不能进行类型检查,因此需要谨慎使用,以避免错误。其次,curry函数不能知道参数的大小,并将它们计数为一个整数的大小。

The paper does not contain an implementation of curry(), but you can imagine how it is implemented using function pointers and variadic functions.

本文不包含curry()的实现,但是可以想象如何使用函数指针和变量函数实现它。

#2


6  

GCC provides an extension for the definition of nested functions. Although this is not ISO standard C, this may be of some interest, since it enables to answer the question quite conveniently. In short, nested function can access the parent function local variables and pointers to them can be returned by the parent function as well.

GCC为嵌套函数的定义提供了一个扩展。虽然这不是ISO标准C,但这可能是有些兴趣,因为它可以很方便地回答这个问题。简而言之,嵌套函数可以访问父函数局部变量,并且父函数也可以返回指向它们的指针。

Here is a short, self-explanatory example:

这里有一个简短的、自我解释的例子:

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

There is however a limitation to this construction. If you keep a pointer to the resulting function, as in

然而,这种结构有一个局限性。如果你有一个指向结果函数的指针,比如in

one_var_func u = partial (add_int, a);

the function call u(0) may result in an unexpected behaviour, as the variable a which u reads was destroyed just after partial terminated.

函数调用u(0)可能会导致意外行为,因为u读取的变量a在部分终止后被销毁。

See this section of GCC's documentation.

请参阅GCC文档的这一部分。

#3


4  

Here's my first guess off the top of my head (may not be the best solution).

这是我的第一个猜想(可能不是最好的解决方案)。

The curry function could allocate some memory off the heap, and put the parameter values into that heap-allocated memory. The trick is then for the returned function to know that it's supposed to read its parameters from that heap-allocated memory. If there's only one instance of the returned function, then a pointer to those parameters can be stored in a singleton/global. Otherwise if there's more than one instance of the returned function, then I think that curry needs to create each instance of the returned function in the heap-allocated memory (by writing opcodes like "get that pointer to the parameters", "push the parameters", and "invoke that other function" into the heap-allocated memory). In that case you need to beware whether allocated memory is executable, and maybe (I don't know) even be afraid of anti-virus programs.

curry函数可以从堆中分配一些内存,并将参数值放在堆分配的内存中。然后,对于返回的函数,要知道它应该从该堆分配的内存中读取它的参数。如果返回函数只有一个实例,那么指向这些参数的指针可以存储在单例/全局变量中。否则如果有多个实例返回的函数,然后我觉得咖喱需要创建的每个实例返回的函数在堆上分配内存(通过编写操作码“得到指针参数”,“把参数”,和“调用其他函数”堆上分配内存)。在这种情况下,您需要注意分配的内存是否可执行,甚至可能(我不知道)害怕杀毒程序。

#4


0  

Here is an approach to doing currying in C. While this sample application is using the C++ iostream output for convenience it is all C style coding.

这里有一种在C中执行同步的方法,尽管这个示例应用程序使用c++ iostream输出,但为了方便起见,它都是C样式的代码。

The key to this approach is to have a struct which contains an array of unsigned char and this array is used to build up an argument list for a function. The function to be called is specified as one of the arguments that are pushed into the array. The resulting array is then given to a proxy function which actually executes the closure of function and arguments.

这个方法的关键是要有一个包含无符号字符数组的结构,这个数组用来建立一个函数的参数列表。要调用的函数被指定为推入数组的参数之一。然后将结果数组提供给一个代理函数,该函数实际上执行函数和参数的闭包。

In this example I provide a couple of type specific helper functions to push arguments into the closure as well as a generic pushMem() function to push a struct or other memory region.

在本例中,我提供了几个类型特定的helper函数,用于将参数推入闭包,以及一个泛型pushMem()函数来推入结构体或其他内存区域。

This approach does require allocation of a memory area that is then used for the closure data. It would be best to use the stack for this memory area so that memory management does not become a problem. There is also the issue as to how large to make the closure storage memory area so that there is sufficient room for the necessary arguments but not so large that excess space in memory or on the stack is taken up by unused space.

这种方法确实需要分配一个内存区域,然后用于闭包数据。最好对这个内存区域使用堆栈,这样内存管理就不会成为问题。还有一个问题是,要让闭包存储内存区域有多大,以便有足够的空间容纳必要的参数,但又不能大到内存或堆栈中的多余空间被未使用的空间占用。

I have experimented with the use of a slightly differently defined closure struct which contains an additional field for the currently used size of the array used to store the closure data. This different closure struct is then used with a modified helper functions which removes the need for the user of the helper functions to maintain their own unsigned char * pointer when adding arguments to the closure struct.

我尝试过使用一个稍微不同定义的闭包结构,它包含一个用于存储闭包数据的数组当前使用的大小的附加字段。然后,将这个不同的闭包结构体与一个修改后的helper函数一起使用,它消除了在向闭包结构中添加参数时,helper函数的用户需要维护自己的无符号char *指针的需要。

Notes and caveats

笔记和警告

The following example program was compiled and tested with Visual Studio 2013. The output from this sample is provided below. I am not sure about the use of GCC or CLANG with this example nor am I sure as to issues that may be seen with a 64 bit compiler as I am under the impression that my testing was with a 32 bit application. Also this would approach would only seem to work with functions that use the standard C declaration in which the calling function handles popping the arguments from the stack after the callee returns (__cdecl and not __stdcall in Windows API).

下面的示例程序是用Visual Studio 2013编译和测试的。这个示例的输出如下所示。我不确定在这个示例中使用GCC或CLANG,也不确定在64位编译器中可能遇到的问题,因为我认为我的测试是在32位应用程序中进行的。此外,这种方法似乎只适用于使用标准C声明的函数,在该声明中,调用函数在被调用者返回(__cdecl,而不是Windows API中的__stdcall)后处理从堆栈中取出的参数。

Since we are building the argument list at run time and then calling a proxy function, this approach does not allow the compiler to perform a check on arguments. This could lead to mysterious failures due to mismatched parameter types which the compiler is unable to flag.

由于我们在运行时构建了参数列表,然后调用代理函数,这种方法不允许编译器对参数进行检查。这可能导致由于编译器无法标记的参数类型不匹配而导致的神秘故障。

Example application

示例应用程序

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

Test output

测试输出

Output from this sample program. The number in parenthesis is the line number in the main where the function call is made.

此示例程序的输出。括号中的数字是函数调用所在的主行号。

 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)