Можно ли как-то использовать вложенные функции в ANSI C? - PullRequest
1 голос
/ 29 мая 2019

У меня есть опыт работы с языками высокого уровня, такими как Java / Kotlin / Scala.Теперь мне нужно создать библиотеку C, но мне довольно сложно работать без замыканий.

В GCC есть замечательное расширение, называемое "вложенными функциями", которое (если я правильно понял) - именно то, что мне нужно.Также интересным выглядит расширение «блоков» Apple.Но мне нужен код, который будет работать с любым компилятором.

Имеется ли какое-либо существующее решение?Я видел несколько проектов, которые позволяют генерировать код на C: https://github.com/dbohdan/compilers-targeting-c. Но мне действительно не нужен другой язык, только одна особенность - замыкания.(Я думаю, что трудно поддерживать совершенно другой язык, и результат кода на C не будет оптимизирован).

UPD Некоторое объяснение, зачем мне замыкания.

Позвольтескажем, у нас есть какая-то функция, connect(void (*on_failed_callback)());.Он каким-то образом управляет соединением, а когда соединение теряется, он вызывает обратный вызов.И это принимает 0 аргументов, но я хочу создать некоторую функцию, которая будет приносить некоторые данные с обратным вызовом.Если я правильно понял, наиболее часто используемое решение - передать обратный вызов с некоторым аргументом 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.

Ответы [ 2 ]

3 голосов
/ 29 мая 2019

В конкретном примере, о котором вы спрашивали, есть подход с 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.
}
0 голосов
/ 29 мая 2019

Имеется ли какое-либо существующее решение?

Это зависит от того, что вы имеете в виду. Стандарт C не предоставляет замыкания, вложенные функции или лямбда-выражения в качестве языковых возможностей. Но вы ничего не можете сделать на каком-то другом языке, который имеет какие-либо или все из них, без чего вы не можете сделать в C. Фактически, некоторые языки, которые имеют , имеют эти функции, сами реализованы в C.

В частности, ваш первый пример - примерно нормальная C-идиома для универсальных обратных вызовов. Функция обратного вызова принимает указатель на объект, содержащий любые необходимые данные, а интерфейс регистрации обратного вызова принимает указатель на соответствующий объект вместе с указателем на функцию. Когда вызывается обратный вызов, на него передается зарегистрированный указатель данных.

Обратите внимание, что, кстати, это более общее средство, чем одни только замыкания, потому что данные, для которых будет работать обратный вызов, не обязательно должны находиться в области действия, в которой определена функция. Среди прочего, это позволяет предоставлять стандартные, повторно используемые реализации обратного вызова, где связь с конкретными данными осуществляется только в точке регистрации обратного вызова. В случае, когда несколько обратных вызовов могут быть одновременно связаны с одним и тем же событием, это также позволяет повторно использовать один и тот же обратный вызов с разными данными для одного и того же события.

...