效仿在C部分功能应用 [英] emulating partial function application in c

查看:79
本文介绍了效仿在C部分功能应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在写假若过的语言,从,部分功能应用效益的一部分的C程序。我需要一个固定的签名提供一个函数指针但有时我需要调用已经写好的函数需要的参数。

I'm writing a C program that would, if had been a part of the language, benefit from, partial function application. I need to supply a function pointer with a fixed signature but sometime I need to call already written function that needs arguments.

我可以写很多小功能,通话对方为:

I can write a lot of small functions, calling each other as:

// example, not actual code
void (*atJoystickLeft)(void); // fixed signature

.. code ..
atJoystickLeft = &func1;
.. code ..

void func1(void) { func2(10, "a string"); } /

我喜欢vould代替:

I vould like instead:

atJoystickLeft = &func2(10, "a string");

这样我就可以处理功能func1的,这只是设置参数调用FUNC2的。

so I can dispose of the function func1 that is only to set parameter for the call to func2.

这将使code。至少,20% - 30%以下。我简单的问题是:有人有在C模拟部分功能应用的把戏? (C99)

That would make the code at least, 20% - 30% smaller. My simple question is: Do someone have a trick for emulating partial function application in C? (C99)

推荐答案

简短的回答是没有,C不支持它。

The short answer is that no, C doesn't support it.

您可以砍在一起的可变参数前端,可以使用一个函数的地址,并创建了一个堆栈帧调用该函数使用这些参数,但它不会是便携 1 。如果你想让它便携,的的自由来修改你要通过该前端调用(成一种形式,不是真正适合直接调用),你可以重新编写所有的其他功能接收 va_alist 作为唯一参数,并获取正确数量/类型的使用的va_arg 参数:

You could hack together a variadic front-end that took the address of a function and created a stack frame to invoke that function with those arguments, but it wouldn't be portable1. If you wanted to make it portable, and were free to modify the other functions you were going to invoke via this front end (into a form that's not really suitable for direct invocation) you could rewrite all of them to receive a va_alist as their sole parameter, and retrieve the correct number/type of parameters using va_arg:

// pointer to function taking a va_alist and return an int:
typedef int (*func)(va_alist); 

void invoke(func, ...) {
    va_alist args;
    va_start(args, func);
    func(args);
    va_end(args);
}

编辑:对不起,因为@missingno指出,我没有做这项工作相当它的目的的方式。这确实应该两个功能:一个取输入并将它们封装入一个结构,而另一个,是以结构并调用预定的功能

Sorry, as @missingno pointed out, I didn't make this work quite the way it was intended. This should really be two functions: one that takes the inputs and wraps them into a structure, and the other that takes the structure and invokes the intended function.

struct saved_invocation { 
     func f;
     argument_list args;
};

saved_invocation save(func f, ...) { 
    saved_invocation s;
    va_alist args;
    s.f = f;
    va_start(args, f);
    va_make_copy(s.args, args);
    va_end(args);
}

int invoke(saved_invocation const *s) { 
    s->f(s->args);
}

有关 va_make_copy ,你会得到更多的进入不标准的东西。它不会是相同的 va_copy - va_copy 通常只存储一个指针的论点开始,这只有保持有效,直到当前函数返回。对于 va_make_copy ,你必须存储所有的实际参数,所以你可以以后再取出来。假设你使用了参数结构我在下面简要说明,你会通过参数使用散步的va_arg ,并使用的malloc (或其他)分配一个节点为每个参数,并建立某种形式的动态数据结构(例如,链表,动态数组)来保存这些参数,直到你准备好使用它们。

For va_make_copy, you'd get into more non-standard stuff. It would not be the same as va_copy -- va_copy normally just stores a pointer to the beginning of the arguments, which only remains valid until the current function returns. For va_make_copy, you'd have to store all the actual arguments so you could retrieve them later. Assuming you used the argument structure I outline below, you'd walk through the arguments using va_arg, and use malloc (or whatever) to allocate a node for each argument, and create some sort of dynamic data structure (e.g., linked list, dynamic array) to hold the arguments until you were ready to use them.

您也不得不加入一些code处理释放内存,一旦你用一个特定的绑定功能完成。它也实际上直接服用的va_list,改变你的函数签名带你制定握住你的参数列表任何类型的数据结构。

You'd also have to add some code to deal with freeing that memory once you were finished with a particular bound function. It would also actually change your function signature from taking a va_list directly, to taking whatever kind of data structure you devised to hold your list of arguments.

[结束编辑]

这将意味着你要调用将需要所有其他功能的签名:

This would mean the signature for every other function you were going to invoke would need to be:

int function(va_alist args);

...然后每个功能将必须通过的va_arg 来检索它的参数,所以(比如)一个函数,要带两个整数作为其参数,并返回它们的和会是这个样子:

...and then each of these functions would have to retrieve its arguments via va_arg, so (for example) a function that was going to take two ints as its arguments, and return their sum would look something like this:

int adder(va_alist args) { 
    int arg1 = va_arg(args, int);
    int arg2 = va_arg(args, int);

    return arg1 + arg2;
}

这有两个明显的问题:第一,尽管我们不再需要为每个功能的独立包装,我们仍可以增加额外的code到每一个功能,让它通过单一的包装被调用。在code尺寸而言,它不可能做的比盈亏平衡好多了,可能很容易成为一个净损失。

This has two obvious problems: first, even though we no longer need a separate wrapper for each function, we still end up adding extra code to every function to let it be invoked via the single wrapper. In terms of code size, it's unlikely to do much better than break even, and might easily be a net loss.

更糟糕,但是,因为我们现在检索所有参数的所有功能作为一个变量参数列表,我们不再得到任何类型的参数检查。如果我们想严重的是,它会(当然)有可能增加一个小包装类型和code来处理,以及:

Much worse, however, since we're now retrieving all the arguments to all the functions as a variable argument list, we no longer get any type checking on the arguments. If we wanted to badly enough, it would (of course) be possible to add a small wrapper type and code to handle that as well:

struct argument { 
    enum type {CHAR, SHORT, INT, LONG, UCHAR, USHORT, UINT, ULONG, /* ... */ };
    union data { 
        char char_data;
        short short_data;
        int int_data;
        long long_data;
        /* ... */
    }
}

然后,当然,你会写更code,检查每个参数的枚举表示,这是预期的类型,并从工会获取正确的数据,如果它是。然而,这会增加一些严重的丑陋调用的功能 - 而不是:

Then, of course, you'd write still more code to check that the enumeration for each argument indicated that it was the expected type, and retrieve the correct data from the union if it was. This, however, would add some serious ugliness to invoking the functions -- instead of:

invoke(func, arg1, arg2);

...你会得到这样的:

...you'd get something like:

invoke(func, make_int_arg(arg1), make_long_arg(arg2));

显然,这的可以的完成。不幸的是,它仍然没有好处 - 降低code原来的目标几乎肯定已经完全丧失。这并不排除包装功能 - 但不是一个简单的包装和简单的调用一个简单的函数,我们结束了一个复杂的功能和复杂的调用,以及额外的code小山上一个值转换为我们的特别参数类型,另一个检索参数的值。

Obviously, this can be done. Unfortunately, it still does no good -- the original goal of reducing code has almost certainly been completely lost. This does eliminated the wrapper function -- but instead of a simple function with a simple wrapper and simple invocation, we end up with a complex function and complex invocation, and a small mountain of extra code to convert a value to our special argument type, and another to retrieve a value from an argument.

虽然有一些可以证明code这样的(例如,写像Lisp的一个跨preTER)的情况下,我认为,在这种情况下,它的工作原理出去的净亏损。即使我们忽略额外的code以添加/使用类型检查,在每个功能的code检索参数,而不是接收他们直接工程以超过我们试图更换包装。

While there are cases that can justify code like this (e.g., writing an interpreter for something like Lisp), I think in this case it works out to a net loss. Even if we ignore the extra code to add/use type checking, the code in each function to retrieve arguments instead of receiving them directly works out to more than the wrapper we're trying to replace.


  1. 不会是便携几乎是轻描淡写 - 你真的不能做到这一点的的下以全 - 你只是需要使用汇编语言,甚至上手

  1. "Wouldn't be portable" is almost an understatement -- you really can't do this in C at all -- you just about have to use assembly language to even get started.

这篇关于效仿在C部分功能应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆