簡體   English   中英

我如何在 C 語言中使用類似於 C++ 中的 lambda 函數的宏和函數指針來實現?

[英]How could I implement in C language using macros and function pointers something similar to lambda functions in C++?

我想在 C 中實現類似於 C++ 中的 lambda 函數的東西(使用宏和函數指針)

我認為我面臨的最大問題是在另一個函數中定義一個函數,而這在 C 中是不可能的。我認為更好的主意是將 lambda 函數(通過宏傳遞)視為詞法塊。

我開始了一些代碼:

#define func_name(line) func##line

#define line __LINE__

#define lambda(body, ret_type, ...)   ret_type  func_name(line)(__VA_ARGS__)  \
                                 {                     \
                                   body;               \
                                 }                     \


//#include <stdio.h>

lambda(  printf("Hello from Lambda\n") ,  void, int a, float b)



int main(void) {
  //printf("Hello World\n");
  return 0;
}

我使用帶有“-E”選項的 gcc 編譯器來查看預處理器輸出:

  void funcline(int a, float b) { printf("Hello from Lambda\n"); }

  int main(void) {

  return 0;
  }

您可以在 C 中創建類似對象的東西,但無法近似 lambda 函數。


我們使用 lambda 函數的主要原因是因為它們是閉包 換句話說,它們是帶有數據的函數。

例如,看看這個 JavaScript 片段:

 function make_closure(x) { return () => x; } let f = make_closure(123); let g = make_closure(456); console.log(f()); // 123 console.log(g()); // 456

請注意,調用者像調用任何其他函數一樣調用該函數。 它不需要知道有任何附加的數據,也不需要做任何特殊的事情來使這些數據可供函數使用。

這與物體形成鮮明對比。 對象(附加到數據的函數)是閉包(附加到函數的數據)的另一面。 調用方法時,調用者必須同時提供函數和數據。

data.method();

我們使用閉包,所以我們不需要這樣做。 它對調用者是完全透明的。

問題是 C 中的函數指針實際上只是地址。 並在那里尋址到只讀存儲器。 [1]實際上無處可附加數據。 沒有傳遞給函數的任何東西可以讓它知道它在哪個上下文中被調用。

所以在 C 中做閉包是不可能的。充其量,我們可以替換

f(...);

f->ptr(f->data, ...);

但這違背了使用閉包的全部意義。 這給了我們 OOP,但不是閉包。

因此,即使我們首先有一種干凈的方法來創建閉包(這本身就是一個大問題),在 C 中也無法實現閉包。


  1. 在可能的系統上。

這是可能的,但它使代碼很難調試。

由於使用這些偽 lambda 的所有函數都必須包含在宏中,因此在預處理期間會刪除其中的所有換行符。 不可能在這樣的函數內部放置斷點,或者一行一行地通過它。


下面是一個用法示例。 實現在答案的最后。 語法的解釋就在示例之后。

在 gcc.godbolt.org 上運行

#include <stdio.h>
#include <stdlib.h>

ENABLE_LAMBDAS(

void example1()
{
    int arr[] = {4,1,3,2,5};

    FUNC(int)(compare)(const void *a, const void *b)
    (
        return *(int*)a - *(int*)b;
    )
    qsort(arr, 5, sizeof(int), compare);

    for (int i = 0; i < 5; i++ )
        printf("%d ", arr[i]);
    putchar('\n');
}

void example2()
{
    int arr[] = {4,1,3,2,5};

    qsort L_(arr, 5, sizeof(int), LAMBDA(int)(const void *a, const void *b)
    (
        return *(int*)a - *(int*)b;
    ));

    for (int i = 0; i < 5; i++ )
        printf("%d ", arr[i]);
    putchar('\n');
}

int main()
{
    example1();
    example2();
}

) // ENABLE_LAMBDAS

請注意封裝整個代碼段的ENABLE_LAMBDAS宏。

此示例使用兩種定義函數/lambda 的方法:

  • FUNC(return_type)(name)(params)(body)只是定義了一個函數。 函數定義移到ENABLE_LAMBDAS的開頭,因此它可以在其他函數內部使用。
  • LAMBDA(return_type)(params)(body)定義了一個偽 lambda。 它的函數定義在ENABLE_LAMBDAS的開頭生成,具有自動選擇的唯一名稱。 LAMBDA...擴展為該名稱。

如果在括號內使用FUNCLAMBDA ,則括號前必須有L_宏。 不幸的是,這是預處理器的限制。

生成的函數總是static


執行:

// Creates a lambda.
// Usage:
//     LAMBDA(return_type)(params)(body)
// Example:
//     ptr = LAMBDA(int)(int x, int y)(return x + y;);
#define LAMBDA LAM_LAMBDA

// Defines a function.
// Usage:
//     FUNC(return_type)(name)(params)(body)
// Example:
//     FUNC(int)(foo)(int x, int y)(return x + y;)
//     some_func(foo);
#define FUNC LAM_FUNC

// Any time a `LAMBDA` or `FUNC` appears inside of parentheses,
//   those parentheses must be preceeded by this macro.
// For example, this is wrong:
//     foo(LAMBDA(int)()(return 42;));
// While this works:
//     foo L_(LAMBDA(int)()(return 42;));
#define L_ LAM_NEST

// `LAMBDA` and `FUNC` only work inside `ENABLE_LAMBDAS(...)`.
// `ENABLE_LAMBDAS(...)` expands to `...`, preceeded by function definitions for all the lambdas.
#define ENABLE_LAMBDAS LAM_ENABLE_LAMBDAS

// Lambda names are composed of this prefix and a numeric ID.
#ifndef LAM_PREFIX
#define LAM_PREFIX LambdaFunc_
#endif

// Implementation details:

// Returns nothing.
#define LAM_NULL(...)
// Identity macro.
#define LAM_IDENTITY(...) __VA_ARGS__
// Concats two arguments.
#define LAM_CAT(x, y) LAM_CAT_(x, y)
#define LAM_CAT_(x, y) x##y
// Given `(x)y`, returns `x`.
#define LAM_PAR(...) LAM_PAR_ __VA_ARGS__ )
#define LAM_PAR_(...) __VA_ARGS__ LAM_NULL(
// Given `(x)y`, returns `y`.
#define LAM_NO_PAR(...) LAM_NULL __VA_ARGS__
// Expands `...` and concats it with `_END`.
#define LAM_END(...) LAM_END_(__VA_ARGS__)
#define LAM_END_(...) __VA_ARGS__##_END

// A generic macro to define functions and lambdas.
// Usage: `LAM_DEFINE(wrap, ret)(name)(params)(body)`.
// In the encloding code, expands to `wrap(name)`.
#define LAM_DEFINE(wrap, ...) )(l,wrap,(__VA_ARGS__),LAM_DEFINE_0
#define LAM_DEFINE_0(name) name,LAM_DEFINE_1
#define LAM_DEFINE_1(...) (__VA_ARGS__),LAM_DEFINE_2
#define LAM_DEFINE_2(...) __VA_ARGS__)(c,

// Creates a lambda.
// Usage: `LAM_LAMBDA(ret)(params)(body)`.
#define LAM_LAMBDA(...) LAM_DEFINE(LAM_IDENTITY, __VA_ARGS__)(LAM_CAT(LAM_PREFIX, __COUNTER__))
// Defines a function.
// Usage: `LAM_FUNC(ret)(name)(params)(body)`.
#define LAM_FUNC(...) LAM_DEFINE(LAM_NULL, __VA_ARGS__)

// `LAM_LAMBDA` and `LAM_FUNC` only work inside of this macro.
#define LAM_ENABLE_LAMBDAS(...) \
    LAM_END( LAM_GEN_LAMBDAS_A (c,__VA_ARGS__) ) \
    LAM_END( LAM_GEN_CODE_A (c,__VA_ARGS__) )

// Processes lambdas and functions in the following parentheses.
#define LAM_NEST(...) )(open,)(c,__VA_ARGS__)(close,)(c,

// A loop. Returns the original code, with lambdas replaced with corresponding function names.
#define LAM_GEN_CODE_A(...) LAM_GEN_CODE_BODY(__VA_ARGS__) LAM_GEN_CODE_B
#define LAM_GEN_CODE_B(...) LAM_GEN_CODE_BODY(__VA_ARGS__) LAM_GEN_CODE_A
#define LAM_GEN_CODE_A_END
#define LAM_GEN_CODE_B_END
#define LAM_GEN_CODE_BODY(type, ...) LAM_CAT(LAM_GEN_CODE_BODY_, type)(__VA_ARGS__)
#define LAM_GEN_CODE_BODY_c(...) __VA_ARGS__
#define LAM_GEN_CODE_BODY_l(wrap, ret, name, ...) wrap(name)
#define LAM_GEN_CODE_BODY_open() (
#define LAM_GEN_CODE_BODY_close() )

// A loop. Generates lambda definitions, discarding all other code.
#define LAM_GEN_LAMBDAS_A(...) LAM_GEN_LAMBDAS_BODY(__VA_ARGS__) LAM_GEN_LAMBDAS_B
#define LAM_GEN_LAMBDAS_B(...) LAM_GEN_LAMBDAS_BODY(__VA_ARGS__) LAM_GEN_LAMBDAS_A
#define LAM_GEN_LAMBDAS_A_END
#define LAM_GEN_LAMBDAS_B_END
#define LAM_GEN_LAMBDAS_BODY(type, ...) LAM_CAT(LAM_GEN_LAMBDAS_BODY_, type)(__VA_ARGS__)
#define LAM_GEN_LAMBDAS_BODY_c(...)
#define LAM_GEN_LAMBDAS_BODY_l(wrap, ret, name, par, ...) static LAM_IDENTITY ret name par { __VA_ARGS__ }
#define LAM_GEN_LAMBDAS_BODY_open()
#define LAM_GEN_LAMBDAS_BODY_close()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM