I want to implement in C something similar to lambda functions from C ( using macros and function pointers)
I think the biggest problem that I am facing is to define a function inside another function, and this thing is not possible in C. I think a better idea is to treat the lambda function ( passed through a macro ) as a lexical block.
I started some code:
#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;
}
I used gcc compiler with "-E" option to see preprocessor output:
void funcline(int a, float b) { printf("Hello from Lambda\n"); }
int main(void) {
return 0;
}
CodePudding user response:
You can create something object-like in C, but lambda functions can't be approximated.
The primary reason we use lambda functions is because they are closures. In other words, they are functions with data attached.
For example, take a look at this JavaScript snippet:
function make_closure(x) {
return () => x;
}
let f = make_closure(123);
let g = make_closure(456);
console.log(f()); // 123
console.log(g()); // 456
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Notice that the caller calls the function as any other function. It doesn't need to know there's any data attached, and it doesn't need to do anything special for this data to be available to the function.
This is in direct contrast with objects. Objects (functions attached to data) are the flip side of closures (data attached to a function). When calling a method, the caller must provide both the function and data.
data.method();
We use closures so we don't need to do this. It's completely transparent to the caller.
The problem is that function pointers in C are literally just addresses. And addresses into read-only memory at that.[1] There's literally nowhere to attach the data. There's nothing passed to the function that would allow it to know in which context it was called.
So it's just impossible to do closures in C. At best, we could replace
f(...);
with something like
f->ptr(f->data, ...);
But that defies the entire point of using a closure. This gives us OOP, but not closures.
So even if we had a clean way of creating the closure in the first place (which is a huge problem in of itself), closures just aren't possible in C.
- On systems where this is possible.
CodePudding user response:
It's possible, but the lambdas will have to be stateless (no captures). You can use preprocessor to move function definitions around, but there is no way to use local variables from one function in another, or to introduce some kind of state into a function.
Also it makes the code very hard to debug. Since all functions using those pseudo-lambdas have to be wrapped in a macro, all line breaks in them are removed during preprocessing. It becomes impossible to place a breakpoint inside of such a function, or to advance through it line by line.
Here is an example of the usage. The implementation is at the end of the answer. The explanation of the syntax is right after the example.
#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
Notice the ENABLE_LAMBDAS
macro wrapping the whole snippet.
This example uses two ways of defining functions/lambdas:
FUNC(return_type)(name)(params)(body)
just defines a function. The function definition is moved to the beginning ofENABLE_LAMBDAS
, so it can be used inside of other functions.LAMBDA(return_type)(params)(body)
defines a pseudo-lambda. A function definition for it is generated at the beginning ofENABLE_LAMBDAS
, with an automatically chosen unique name.LAMBDA...
expands to that name.
If FUNC
or LAMBDA
are used inside of parentheses, the parentheses must be preceeded by the L_
macro. This is a limitation of the preprocessor, unfortunately.
The generated functions are always static
.
Implementation:
// 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()