简体   繁体   中英

Can I somehow use nested functions in ANSI C?

I have background with high level languages, such as Java/Kotlin/Scala. Now I need to create a C library, but it's pretty hard for me to work without closures.

There is the nice extension in GCC, called "nested functions", that (if I correctly understood) is exactly what I need. Also the Apple "blocks" extension looks interesting. But I need a code, that will work with any compiler.

Does any existing solution present? I've seen some projects, that allow generating C code: https://github.com/dbohdan/compilers-targeting-c . But I really don't need another language, just one feature - closures. (I think that it hard to maintain completely different language and result C code will be not optimized).

UPD Some explanation, why do I need closures.

Let say we have some function, connect(void (*on_failed_callback)()); . It manages a connection in some way, and when the connection is lost, it calls the callback. And it takes 0 arguments, but I want to create some function that will bring some data with callback. If I've correctly understood, the most common used solution is to pass the callback with some arg connect(void (*on_failed_callback)(void *callback_arg), void* arg); . But it leads to a boilerplate code, that can be fixed by nested functions.

So next code:

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

Can be simplified to next:

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

I mean, I want some tool/app, that analyzes my code with closures/nested functions and generates pure ANSI C code.

For the specific example you have asked about there is an approach with C that achieves something similar to a closure in that it packages a function pointer with the data needed to be used with the function.

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.
}

And if at the time that the function package is used, you would like to add additional data, you could do so along the lines of the following.

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.
}

Does any existing solution present?

It depends on what you mean. Standard C does not provide closures, nested functions, or lambdas / blocks as language features. But there is nothing you can do in some other language that has any or all of these that you cannot do in C without. In fact, some languages that do have these features are themselves implemented in C.

In particular, your first example is approximately the normal C idiom for generic callbacks. The callback function accepts a pointer to an object containing any data it needs, and the callback-registration interface accepts a pointer to the appropriate object together with a pointer to the function. When the callback is triggered, the registered data pointer is passed to it.

Note, by the way, that this is a more general facility than closures alone afford, because the data on which the callback will operate do not need to be in scope where the function is defined. Among other things, this permits standard, reusable callback implementations to be provided, where the association with specific data is made only at the point of callback registration. In the event that multiple callbacks can be simultaneously associated with the same event, it also allows the same callback to be reused, with different data, for the same event.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM