From 5dbc5ba15ef901019f4025b44da531dcb046854c Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Sun, 7 Jan 2024 09:57:37 +0100 Subject: [PATCH] Add lambda emulation --- engine/CMakeLists.txt | 1 + engine/breeze.h | 1 + engine/breeze/util/lambda.h | 63 +++++++++++++++++++++++++++++++++++++ engine/tests/CMakeLists.txt | 3 ++ engine/tests/lambda_test.c | 42 +++++++++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 engine/breeze/util/lambda.h create mode 100644 engine/tests/lambda_test.c diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 02f16e9..9189e27 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -69,6 +69,7 @@ set(BreezeHeaders breeze/util/array.h breeze/util/heap.h + breeze/util/lambda.h breeze/util/object_pool.h breeze/util/spatial_grid.h breeze/util/string.h diff --git a/engine/breeze.h b/engine/breeze.h index 874d3dc..a0e3dd3 100644 --- a/engine/breeze.h +++ b/engine/breeze.h @@ -13,6 +13,7 @@ #include "breeze/util/array.h" #include "breeze/util/heap.h" +#include "breeze/util/lambda.h" #include "breeze/util/object_pool.h" #include "breeze/util/spatial_grid.h" #include "breeze/util/string.h" diff --git a/engine/breeze/util/lambda.h b/engine/breeze/util/lambda.h new file mode 100644 index 0000000..b61e9c8 --- /dev/null +++ b/engine/breeze/util/lambda.h @@ -0,0 +1,63 @@ +#ifndef BREEZE_LAMBDA_H +#define BREEZE_LAMBDA_H + +#include "../memory/memory.h" + +typedef struct BzLambda BzLambda; + +typedef void (*BzLambdaFn)(BzLambda *lambda, ...); + +#define BZ_LAMBDA_STACK_SIZE 52 +struct BzLambda { + BzLambdaFn fn; + + i32 captureSize; + /* stack contents */ + u8 stack[BZ_LAMBDA_STACK_SIZE]; +}; +static_assert(sizeof(BzLambda) == 64, ""); + +#define BZ_LAMBDA(lambdaName) \ + (BzLambda) { \ + .fn = (BzLambdaFn) lambdaName \ + } + +#define BZ_LAMBDA_BEGIN_CAPTURE(lambda) \ + do { \ + BzLambda *_lam = (lambda); \ + _lam->captureSize = 0; \ + } while (0) + +#define BZ_LAMBDA_CAPTURE(lambda, var) \ + do { \ + BzLambda *_lam = (lambda); \ + i32 _stackSize = _lam->captureSize; \ + i32 _varSize = sizeof(var); \ + assert(_stackSize + _varSize < (i32) sizeof(_lam->stack)); \ + void *_location = &(var); \ + bzMemCpy(&_lam->stack[_stackSize], _location, _varSize); \ + _lam->captureSize += _varSize; \ + } while (0) + +#define BZ_LAMBDA_UNPACK_START() int32_t _stackOffset = 0 +#define BZ_LAMBDA_UNPACK(type, name) \ + type name = (_stackOffset += sizeof(type), \ + assert(_stackOffset <= _lam->captureSize), \ + *(type *) &_lam->stack[_stackOffset - sizeof(type)]) + +#define BZ_LAMBDA_CAST_RET(retType) (retType(*)(BzLambda *, ...)) + +/* + * Note: C99 requires at least one argument for "..." in variadic macro, + * so we need to have 2 variants of CLAMBDA_CALL and CLAMBDA_DECL. + */ + +#define BZ_LAMBDA_CALL(lambda, retType, ...) ((BZ_LAMBDA_CAST_RET(retType)((lambda)->fn))(lambda, ##__VA_ARGS__)) +#define BZ_LAMBDA_NOP_CALL(lambda, retType) ((BZ_LAMBDA_CAST_RET(retType)((lambda)->fn))(lambda)) + +#define BZ_LAMBDA_DECL(lambdaName, ...) lambdaName(BzLambda *_lam, ##__VA_ARGS__) +#define BZ_LAMBDA_NOP_DECL(lambdaName) lambdaName(BzLambda *_lam) + + +#endif // BREEZE_LAMBDA_H + diff --git a/engine/tests/CMakeLists.txt b/engine/tests/CMakeLists.txt index 9ca04e3..234ba44 100644 --- a/engine/tests/CMakeLists.txt +++ b/engine/tests/CMakeLists.txt @@ -14,6 +14,9 @@ target_link_libraries(ecs_defer_test LINK_PRIVATE Breeze) add_executable(heap_test heap_test.c) target_link_libraries(heap_test LINK_PRIVATE Breeze) +add_executable(lambda_test lambda_test.c) +target_link_libraries(lambda_test LINK_PRIVATE Breeze) + add_executable(array_test array_test.c) target_link_libraries(array_test LINK_PRIVATE Breeze) diff --git a/engine/tests/lambda_test.c b/engine/tests/lambda_test.c new file mode 100644 index 0000000..1000ea5 --- /dev/null +++ b/engine/tests/lambda_test.c @@ -0,0 +1,42 @@ +#include +#include + +int BZ_LAMBDA_DECL(lambdaAdd, int a) { + BZ_LAMBDA_UNPACK_START(); + BZ_LAMBDA_UNPACK(int, x); // still have access to x + + return a + x; +} + +void BZ_LAMBDA_NOP_DECL(printArgs) { + BZ_LAMBDA_UNPACK_START(); + BZ_LAMBDA_UNPACK(int, argc); + BZ_LAMBDA_UNPACK(char **, argv); + + printf("Program arguments:\n"); + for (int i = 0; i < argc; i++) { + printf("\t%s\n", argv[i]); + } +} + +BzLambda foo(int x) { + BzLambda lam = BZ_LAMBDA(lambdaAdd); + BZ_LAMBDA_BEGIN_CAPTURE(&lam); + BZ_LAMBDA_CAPTURE(&lam, x); + // x goes out of scope + return lam; +} + +int main(int argc, char **argv) { + + BzLambda lambda1 = foo(8); + printf("%d\n", BZ_LAMBDA_CALL(&lambda1, int, 1)); + + BzLambda printLam = BZ_LAMBDA(printArgs); + BZ_LAMBDA_BEGIN_CAPTURE(&printLam); + BZ_LAMBDA_CAPTURE(&printLam, argc); + BZ_LAMBDA_CAPTURE(&printLam, argv); + BZ_LAMBDA_NOP_CALL(&printLam, void); + + return 0; +}