【问题标题】:Can I somehow use nested functions in ANSI C?我可以在 ANSI C 中以某种方式使用嵌套函数吗?
【发布时间】:2019-10-15 03:02:33
【问题描述】:

我有高级语言的背景,例如 Java/Kotlin/Scala。现在我需要创建一个 C 库,但是没有闭包我很难工作。

GCC 中有一个很好的扩展,称为“嵌套函数”,(如果我理解正确的话)正是我所需要的。苹果的“blocks”扩展看起来也很有趣。但我需要一个代码,它适用于任何编译器。

是否存在任何现有的解决方案?我看过一些允许生成 C 代码的项目:https://github.com/dbohdan/compilers-targeting-c。但我真的不需要另一种语言,只需要一个特性——闭包。 (我认为很难维护完全不同的语言,结果 C 代码不会优化)。

UPD 一些解释,为什么我需要闭包。

假设我们有一些功能,connect(void (*on_failed_callback)());。它以某种方式管理连接,当连接丢失时,它会调用回调。它需要 0 个参数,但我想创建一些函数,它会带来一些带有回调的数据。如果我理解正确,最常用的解决方案是使用一些 arg connect(void (*on_failed_callback)(void *callback_arg), void* arg); 传递回调。但它会导致样板代码,可以通过嵌套函数来修复。

接下来的代码:

void connect(void (*on_failed_callback)(void* arg), void* arg) {
    ...
    on_failed_callback(arg);
}

void print_error(char* error) {
    printf(error);
}

void main() {
    char *msg = "Failed connection";
    ...
    connect(print_error, msg);
}

可以简化为下一个:

void connect(void (*on_failed_callback)()) {
    ...
    on_failed_callback();
}

void print_error(char* error) {
    printf(error);
}

void main() {
    char* msg = "Failed connection";
    ...
    void callback() {
       print_error(msg);
    }
    connect(callback);
}

我的意思是,我想要一些工具/应用程序,用闭包/嵌套函数分析我的代码并生成纯 ANSI C 代码。

【问题讨论】:

  • 没有。而且我看不出没有嵌套函数可以做什么。 C 是命令式语言,与函数式语言有很大不同。然而,我发现函数式编程知识对理解递归和函数纯度等概念有很大帮助,这可以增强而不是削弱 C 技能。
  • ANSI C(1990 年更名为 ISO C)不支持嵌套函数或闭包。因此,如果您使用 gcc 扩展,您的代码将无法与其他编译器一起使用。
  • 无论如何,C 是一种相当低级的语言。从 70 年代的 K&R C 开始,它就旨在对编译器友好,而不是为程序员节省一些打字。如果你想要更多好东西,你将不得不转向 C++...
  • GCC 的嵌套函数不是正确的闭包,它们只能在其存活时引用它们的环境!

标签: c nested-function


【解决方案1】:

对于您询问的具体示例,有一种使用 C 的方法可以实现类似于闭包的功能,因为它将函数指针与函数需要使用的数据打包在一起。

typedef struct {
    char sBuff[128];              // data for the function to use
    void (* func) (char* error);  // pointer to function to execute
} ErrorMsg;

void connect(ErrorMsg myMsg) {
    ...
    myMsg.func(myMsg.sBuff);  // call the function with the packaged data
}

// the function that we will be encapsulating with the data to be
// used.
void print_error(char* error) {
    printf(error);
}

void main() {
    ErrorMsg msg = {"Failed connection", print_error} ;
    …
    connect(msg);  // invoke our function package and its data.
}

如果在使用函数包时,您想添加额外的数据,您可以按照以下方式进行。

typedef struct {
    char sBuff[128];              // packaged data for the function to use
    void (* func) (char* error, int iExtra);  // pointer to function to execute
} ErrorMsg;

void connect(ErrorMsg myMsg) {
    ...
    myMsg.func(myMsg.sBuff, errno);  // call the function with the packaged data and extra info
}

// the function that we will be encapsulating with the data to be
// used.
void print_error(char* error, int iExtra) {
    printf(error, iExtra);
}

void main() {
    ErrorMsg msg = {"Failed connection: errno %d", print_error} ;
    …
    connect(msg);  // invoke our function package and its data.
}

【讨论】:

  • 是的,答案很好。这不仅是常见的 C 实践,而且比一些本地化的“闭包”元编程要好得多,后者仅用于在对象和一些不相关的调用者之间创建紧密耦合。
【解决方案2】:

是否存在任何现有的解决方案?

这取决于你的意思。标准 C 不提供闭包、嵌套函数或 lambdas / 块作为语言特性。但是,在其他语言中,没有任何东西是你无法在 C 中完成的。事实上,一些确实具有这些特性的语言本身是用 C 实现的。

特别是,您的第一个示例大致是通用回调的正常 C 习惯用法。回调函数接受指向包含它需要的任何数据的对象的指针,回调注册接口接受指向适当对象的指针以及指向函数的指针。触发回调时,将注册的数据指针传递给它。

顺便说一下,这是一个比单独闭包提供的更通用的工具,因为回调将操作的数据不需要在定义函数的范围内。除此之外,这允许提供标准的、可重用的回调实现,其中与特定数据的关联仅在回调注册点进行。如果多个回调可以同时与同一个事件关联,它还允许为同一个事件重复使用具有不同数据的同一个回调。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    • 2023-03-18
    • 2018-05-24
    • 2012-06-14
    相关资源
    最近更新 更多