如何提升函数以在 C 中获取额外参数?

How to lift a function to take an extra parameter in C?

(我明白C并不是要以函数式的方式使用的。但是,学习了函数式编程后,我很难有不同的想法。)

鉴于这些限制:

我们能不能想办法提升一个函数 f 来带一个额外的参数来拟合一个原型,比如这个新参数在 f 执行的时候会被忽略?

下面是我想做的事情的详细信息:

我想拟合一个函数 f,它的类型是:

f :: void f(char *s)

在一个函数g中,它接受一个函数作为参数(称之为arg),其类型是:

arg :: void f(unsigned int i, char *s)

因此,g的类型是:

g :: void g(void (*f) (unsigned int, char))

haskell 中的解决方案如下:

g (const f)

这有可能吗,也许需要某种宏魔法?

编辑:为了更好地理解,这里是实际代码。 ft_striter 的正文需要完成。目的是:将函数 f 应用于字符串的每个字符,使用或不使用其索引 i.

void        ft_striter(char *s, void (*f) (char *s))
{
    ft_striteri(?);
}

static void ft_striteri_helper(char *s, unsigned int i, void (*f)(unsigned int, char*))
{
    if (*s) 
    {
        f(i, s);
        ft_striteri_helper(s + 1, i + 1, f);
    }
}

void        ft_striteri(char *s, void (*f)(unsigned int, char*))
{
    ft_striteri_helper(s, 0, f);
}

由于您只想接受并忽略额外的参数,您可以通过创建一个具有所需签名的包装函数来完成此操作,该函数只是委托给原始函数:

void f2(unsigned int i, char *s) {
    f(s);
}

在范围内正确声明该函数后,您可以简单地调用

g(f2);

函数 f2() 如果您愿意,可以将其声明为静态的,这样它对于定义它的文件之外的代码是不可见的。不需要嵌套。

好吧,让我们考虑几个选项。

首先,C 没有 lambda 表达式,尽管最新的 C++ 标准有。如果这对您来说是一个主要问题,那么考虑使用 C++ 而不是 C 可能是值得的。

C可以定义并传递function pointers。因此将一个函数作为参数传递给另一个函数是完全合法的。

你也可以自己定义functions to take a variable number of arguments,可能对你也有用

C 宏也很有用。例如,您可以这样做:

#define f1( x )   g( (x), 1 )
#define f2( x )   g( (x), 2 )

int g( int x, int y )
{
    return ( x + y ) ;
}

你甚至可以用宏做一些聪明的事情来允许 macro overloading。就编码风格而言,这并不完全理想,但这是可能的。

所以这里可能有一些工具可以满足您的需求。

更新

OP 在我编写我的内容时向他的 post 添加了更新,所以如果我理解他的目标,这里是一个粗略的建议。

OP 似乎希望所选函数能够访问可以在其他地方设置的索引变量,他可以尝试使用结构作为参数。也许是这样的:

typedef struct mystring_s {
    char *s ;
    int i ;
    } mystring_t ;

void f1( mystring_t *strp )
{
    /* can access strp->s and strp->i */
}

/* and similar for other functions f2, f2, etc. */

/* And to use this you can call any function using
 */

void g( void (*fn)( mystring_t * ), mystring_t *strp )
{
    (*fn)( strp ) ;
}

void set_i( mystring_t *strp, int v )
{
    strp->i = v ;
}

/* for example */

mystring_t s ;

/* set up s */

set_i( &s, 11 ) ;
g( &f1, &s ) ;
set_i( &s, 31 ) ;
g( &f2, &s ) ;

当然他也可以尝试使用全局值(他显然不想或认为不可能),他甚至可以将函数指针与数据一起存储。

很难确定这对 OP 来说是否是一个可行的想法,因为他真的在尝试做一些 C 不适合做的事情。我认为问题在于他脑子里有一个设计理念,这对 C 来说是一个糟糕的选择。最好看看这个需求是如何产生的,并将整体设计修改为对 C 更友好的东西,而不是尝试并让 C 做一些事情来取代 Haskell 的能力。

您不能在标准的可移植 C 中实现它。问题是函数 g。设计错误。它应该有类型

void g(void (*f) (void *, unsigned int, char), void *ctxt)

它应该将其 ctxt 参数作为第一个参数传递给它发出的任何 f 调用。

现在你可以用像

这样的代码实现你想要的
struct const_ctxt {
    void (*fun)(char *);
}

void const(void *ctxt, unsigned int i, char *s)
{
    ((struct const_ctxt *)ctxt)->fun(s);
}

void call_g_using_const_f(void (*f)(char *))
{
    struct const_ctxt *ctxt = malloc(sizeof (struct const_ctxt));
    ctxt->fun = f;
    g(const, (void *)ctxt);
    free(ctxt);
}

警告:如果 g 的参数可能会超出 g 的动态范围,那么您将需要找到另一种策略来管理 ctxt 的分配。

g 采用一对代码指针和数据 "context"/"environment" 指针的模式是高级语言通常如何实现闭包。