我想在 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;
}
uj5u.com熱心網友回復:
您可以在 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 中也無法實作閉包。
- 在可能的系統上。
uj5u.com熱心網友回復:
這是可能的,但 lambdas 必須是無狀態的(沒有捕獲)。您可以使用前處理器來移動函式定義,但無法在另一個函式中使用區域變數,或將某種狀態引入函式。
這也使得代碼很難除錯。由于使用這些偽 lambda 的所有函式都必須包含在宏中,因此在預處理期間會洗掉其中的所有換行符。不可能在這樣的函式內部放置斷點,或者一行一行地通過它。
下面是一個用法示例。實作在答案的最后。語法的解釋就在示例之后。
Run on 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...擴展到該名稱。
如果FUNC或LAMBDA在括號內使用,則括號之前必須是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()
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/356314.html
上一篇:如果y==0,如何讓C接受
