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

如何在C中提升函数以获取额外参数?,c,haskell,functional-programming,clang,C,Haskell,Functional Programming,Clang,(我知道C不打算以函数式的方式使用。但是,在学习函数式编程之后,我很难用不同的方式思考。) 鉴于这些限制: 没有嵌套函数,因为clang禁止它(因此没有lamda表达式) 没有全局变量 我们能想出一种提升函数f的方法来获取一个额外的参数来适应原型吗,比如当f被执行时,这个新参数将被忽略 下面是我想做的详细内容: 我想拟合一个函数f,其类型为: f :: void f(char *s) arg :: void f(unsigned int i, char *s) 在将函数作为参数(称为ar

(我知道C不打算以函数式的方式使用。但是,在学习函数式编程之后,我很难用不同的方式思考。)

鉴于这些限制:

  • 没有嵌套函数,因为clang禁止它(因此没有lamda表达式)
  • 没有全局变量
我们能想出一种提升函数f的方法来获取一个额外的参数来适应原型吗,比如当f被执行时,这个新参数将被忽略

下面是我想做的详细内容:

我想拟合一个函数f,其类型为:

f :: void f(char *s)
arg :: void f(unsigned int i, char *s)
在将函数作为参数(称为arg)的函数g中,其类型为:

f :: void f(char *s)
arg :: void f(unsigned int i, char *s)
因此,g的类型为:

g :: void g(void (*f) (unsigned int, char))
haskell的解决方案如下:

g (const f)
这有可能吗,也许用某种宏魔法

编辑:为了更好地理解,下面是实际代码。ft_Stritter的主体需要完成。其目的是:使用或不使用其索引i,将函数f应用于字符串的每个字符

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可以定义和传递。因此,将一个函数作为参数传递给另一个函数是完全合法的

您还可以定义,这可能对您也很有用

C宏也很有用。例如,您可以执行以下操作:

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

int g( int x, int y )
{
    return ( x + y ) ;
}
你甚至可以用宏来做一些聪明的事情。就编码风格而言,这并不理想,但这是可能的

因此,可能有一些工具在那里得到你想要的

更新 在我撰写我的文章时,OP给他的文章添加了一个更新,所以如果我理解他的目标,这里是一个粗略的建议

看起来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
s的分配


g
取一对代码指针和一对数据“上下文”/“环境”指针的模式是高级语言通常实现闭包的方式。

f
固定的还是可变的?回答这个问题的一种方法是找到一种方法,让arg可以看到f,能够在函数体中调用f。f是相对于g@qleguennec,不,.@qleguenec那么您在设计回调时可能犯了一个错误。如果您可以发布您的实际代码,我们可以尝试找到解决方案,而不是解决一个在C.f中通常无法解决的元问题wrote@qleguennec,为什么不呢?当然
f()
f2()
是否可见并不直接取决于
f2()
本身。@qleguenec这毫无意义。也许你应该编辑你的问题,并提供骨架C程序来演示这个问题。因为f是相对于g的,即使它是在其他地方定义的,在任何时候,我都会这样使用它:g(这个函数)。但是,我将在g的主体中把它称为“f”。如果你认为这更有意义,我可以用我试图编写的真实代码编辑我的问题。Varargs函数与前面提到的问题无关,因为涉及的函数不是Varargs函数。通过varargs或其他方式,任何包装都不允许类型为
void(*)(unsigned int,char*)
的函数指针携带指向的函数应该调用的其他函数的信息。这必须由函数实现来确定。宏对于OP的情况来说是不有用的,因为他需要动态的解决方案,而不是静态的解决方案。可能C++和lambdas是一种替代方案,但是OP已经断言它不是。除非你有理由在这一点上反驳他,否则建议lambdas(或C++)是没有用的。这可能是最接近的答案,但不幸的是,这不可行,因为原型是不可变的,它必须完全符合规定。然而,我将验证这个答案。@qleguenec在C中,涉及回调的设计良好的库经常使用这种习惯用法来获得类似于函数闭包的东西。闭包可以实现为一个完全抽象的lambda术语(没有自由变量)与一个环境(提供第一个变量的值)配对