{"id":13846348,"url":"https://github.com/AlexCeleste/C99-Lambda","last_synced_at":"2025-07-12T07:32:29.042Z","repository":{"id":65794009,"uuid":"4995973","full_name":"AlexCeleste/C99-Lambda","owner":"AlexCeleste","description":"Purely evil preprocessor macros adding anonymous functions and closures to ISO C99","archived":false,"fork":false,"pushed_at":"2015-05-01T15:54:16.000Z","size":116,"stargazers_count":146,"open_issues_count":0,"forks_count":11,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-04-04T23:06:33.787Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlexCeleste.png","metadata":{"files":{"readme":"README","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-07-11T22:22:41.000Z","updated_at":"2024-03-12T21:03:32.000Z","dependencies_parsed_at":"2023-02-10T11:25:10.736Z","dependency_job_id":null,"html_url":"https://github.com/AlexCeleste/C99-Lambda","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCeleste%2FC99-Lambda","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCeleste%2FC99-Lambda/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCeleste%2FC99-Lambda/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlexCeleste%2FC99-Lambda/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlexCeleste","download_url":"https://codeload.github.com/AlexCeleste/C99-Lambda/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225807259,"owners_count":17527221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T18:00:30.708Z","updated_at":"2024-11-21T21:30:25.072Z","avatar_url":"https://github.com/AlexCeleste.png","language":"C","funding_links":[],"categories":["C language extensions","C"],"sub_categories":[],"readme":"#\n# C99-Lambda: nested functions, lambdas, and closures, in ISO C99\n# ---------------------------------------------------------------\n#\n# originally by Alex \"Yasha\" Gilding, 2012\n# \n# This project is placed in the public domain, and may be used for any purpose,\n# without any conditions attached or attribution necessary. You can find a copy\n# of this licence here:\n#\n# ...oh wait.\n#\n# Files supplied AS IS. No warranty is implied and no liability is accepted for\n# any consequences of using this code. Using code like this in a real project\n# is highly likely to result in a sound thrashing from your colleagues.\n#\n\n\nRequirements:\n-------------\n\nC99 conformant (or later) preprocessor. Works with GCC, should work with Clang.\nDoes not appear to work with TCC or PCC. Unlikely to work at all with MSVC. The\ncontinuation-machine is quite demanding on the preprocessor.\n\nA willing suspension of disbelief. All code examples in this readme really are\nstandard-conformant ISO C, but it may be hard to believe. This is probably a\nbad thing.\n\n\nUsage:\n------\n\n#include \"c_lambda.h\"\n\nThat's all. It's just a tiny test project, after all. Now your namespace is\npolluted with a bunch of bizarre macros.\n\n\nPublic syntax extension macros:\n-------------------------------\n\nThe macros intended to be used at the top-level - i.e. in user code - are as\nfollows:\n\n\n namespace(\u003cname\u003e, ...\u003cbody\u003e)\n func(\u003creturn type\u003e, \u003cname\u003e, ...\u003cbody\u003e)\n\nAny code making use of the inline functions must appear inside one of these two\nblock constructs. These must appear at the global scope level, as they provide\nthe framework out of which the inner functions are lifted. They also provide a\nroot naming scheme for any contained functions. \"func\" is a convenience version\nthat links the namespace to the scope of a normal C function. Example:\n\n namespace(myFunctionList,\n   typedef int(* fptr)(void);\n   fptr functions[] = {\n     fn(int, (void), { return 1; }),  // Simple function literals\n     fn(int, (void), { return 2; }),\n     fn(int, (void), { return 3; })\n   };\n )\n \n func(int, myGlobalFunc, (int blah) {\n     //...code here, presumably involving inner functions\n })\n\nAll namespaces must be unique. Tying namespacing to normal toplevel functions\nhelps enforce this, while also abstracting the need to think about a naming\nscheme for anonymous objects. Since namespaces must be global, they should not\nnest and should not appear within function scopes.\n\n\n fn(\u003creturn type\u003e, \u003cargs\u003e, ...\u003cbody\u003e)\n\nThe main macro for generating an inline anonymous function. This cannot appear\noutside a func() or namespace() block. Inner functions may nest to any depth\n(within reason and the limits of the continuation machine). Example:\n\n typedef int(* fptr)(int);\n func(fptr, someFunc, (void) {\n     return fn(int, (int a), {\n         fptr f = fn(int, (int b), { return b * 6; });\n         return a * f(a + 1);\n     });\n })\n\nInline functions declared in this way may *not* appear within parentheses, for\nany reason, even as a macro argument (don't do that). An exception exists, and\nis below, but for the most part parentheses interfere with the macro expansion.\nAnonymous functions using this form *may* appear in most other contexts:\n - as a return value (shown above)\n - in a braced initializer list, as above in an array, or for a struct\n - as the RHS of an assignment (shown above)\n - in the function-call position, as long as there are no containing parens.\n   Example:\n\n //...\n foo = fn(int, (int a, int b), { return (a + b) * (b - a); })(4, 5);\n fn(void, (int c), { printf(\"Received c: %d\\n\", c); })(47);\n //...\n\n\n defn(\u003creturn type\u003e, \u003cname\u003e, \u003cargs\u003e, ...\u003cbody\u003e)\n\nDefine a function, named and visible in the local scope. The same rules apply\nas for fn() regarding parentheses and namespace blocks. Unlike fn(), defn() is\na statement, not an expression; as with a normal function declaration, it cannot\nbe called in-place or used as a value. Example:\n\n // Same code as above, using defn() instead of fn()\n defn(int, helper, (int a, int b), {\n     return (a + b) * (b - a);\n })\n foo = helper(4, 5);\n //...\n\nThe name declared by defn() is a normal local constant containing a function\npointer, and can be used from then on as one would in normal C code.\n\n\n cl(\u003creturn type\u003e, \u003cargs\u003e, \u003cfree variables\u003e, ...\u003cbody\u003e)\n\nThis macro creates a closure object in-place, over the variables named in the\n\"second args list\". The expression is subject to the same rules as fn() - must\nappear within a namespace block, may not appear within parentheses - but also,\nbecause it does not return a raw C function pointer, may not appear in the call\nposition (and therefore C syntax cannot call it like a function). The returned\nvalue is a pointer to a stack-allocated struct (declared at the same external\nsite as the function body), containing the function pointer, the total size of\nthe object, and the values of the closed-over variables. Example:\n\n // User supplies a big enough buffer here...\n func(void, makeAdder, (void * out_buf, int add) {\n     closure_type(int, (int)) c = cl(int, (int x), ((int, add)), {\n                                      return x + _env-\u003eadd;\n                                  });\n     memcpy(out_buf, c, c-\u003e_size);\n })\n \n // Close over two variables, no args\n int x = 42, y = 47;\n cl(void, (...), ((int, x), (int, y)), {\n     printf(\"%d, %d\\n\", _env-\u003ex, _env-\u003ey);\n });\n\nIn the second example, the closure takes no arguments; to indicate this, the\nvariadic tail should be used instead of \"void\". Also note how the free variable\nlist has a slightly more complicated format than the argument list.\n\nTo invoke a closure, call its _fun field with itself as the first argument. Any\nother arguments may follow. Example:\n\n // Invoke a closure\n closure_type(int, (int)) c = ...\n c-\u003e_fun(c, 6);\n\n\n closure_type(\u003creturn type\u003e, \u003carg types\u003e)\n\nThis macro expands to an anonymous struct pointer type suitable for punning as\nthe type of a closure object. The type exposes the _fun and _size fields needed\nfor general usage of the closure object. Example:\n\n typedef closure_type(int, (int, int)) Clos;\n Clos c = cl(int, (int x, int y), ((int,a)), { ... });\n Clos d = malloc(c-\u003e_size); memcpy(d, c, c-\u003e_size);\n int x = d-\u003e_fun(d, 8, 10);\n \n closure_type(int, (int)) c1 = cl(int, (int x), ...);\n\nNote that C's typing rules mean that every type created by closure_type is\ntechnically distinct, so it should mostly be used with typedef rather than\nin-place.\n\n\n _fe1(\u003cfunction to call\u003e, \u003clambda\u003e, \u003cother args\u003e...)\n _fe2(\u003cfunction to call\u003e, \u003carg\u003e, \u003clambda\u003e, \u003cother args\u003e...)\n _fe3(\u003cfunction to call\u003e, \u003carg\u003e, \u003carg\u003e, \u003clambda\u003e, \u003cother args\u003e...)\n _fe4(\u003cfunction to call\u003e, \u003carg\u003e, \u003carg\u003e, \u003carg\u003e, \u003clambda\u003e, \u003cother args\u003e...)\n _cle1(\u003cfunction to call\u003e, \u003cclosure\u003e, \u003cother args\u003e...)\n _cle2(\u003cfunction to call\u003e, \u003carg\u003e, \u003cclosure\u003e, \u003cother args\u003e...)\n _cle3(\u003cfunction to call\u003e, \u003carg\u003e, \u003carg\u003e, \u003cclosure\u003e, \u003cother args\u003e...)\n _cle4(\u003cfunction to call\u003e, \u003carg\u003e, \u003carg\u003e, \u003carg\u003e, \u003cclosure\u003e, \u003cother args\u003e...)\n\nThese macros provide the sole exception to the parenthesis rule for anonymous\nfunctions and inline closures. An anonymous function or closure may appear in\nthe respective position marked \"lambda\" or \"closure\" for each form, using only\nthe procedure body rather than the fn() or cl() macro (see below: basically\njust drop the \"fn\" or \"cl\").\n\nThe function in position zero will be called with the lambda as an argument,\nalong with any arguments appearing in the positions before or after it. If the\n_feN form is being used, the function should accept a function pointer; and if\nthe _cleN form is being used, it should accept a closure pointer.\n\nNo syntactic form is provided with the lambda in position zero, since a fn()\nform lambda may be called using direct C syntax (shown above), while doing so\nwith a closure is frankly more or less pointless.\n\nThe _feN and _cleN forms are statements, not expressions, and do not return any\nvalue. To return a value, the easiest option would be to use an out-parameter\n(possibly defining a wrapper function to pass it through).\n\nExamples:\n\n // Assume gforeach and gmap are higher-order functions...\n _fe2(gforeach, glist((char*[]){\"foo\", \"bar\", \"baz\"}), (void, (char * c), {\n     printf(\"%s: %d\\n\", c, strlen(c));\n }));\n \n GList * out; int k = 2;\n _cle2(gmap, glist((int[]){ 1, 2, 3, 4 }), (int, (int n), ((int, k)), {\n     return n * _env-\u003ek;\n }), \u0026out);\n // \"out\" now contains { 2, 4, 6, 8 }\n\n\nImplementation\n--------------\n\nThe main goal of the project is to find a way to \"lift\" lambdas out of their\noriginal context, and move them to the global scope to be declared, along with\nany modifications and extras required for them to work, ahead of their point of\nuse. There are three main components to this.\n\nFirst, code must be broken up into a list of lambdas and non-lambda sections.\nThe enclosing namespace block wraps its contents in a \"non-lambda\" marker. When\nan inner function is encountered, it closes the wrapping marker, wraps the body\nof the inner function in a function marker, and reopens the wrapping marker.\n\nThus this:\n\n func(void, foo, (void) {\n   fptr g = fn(void, (void), { 0; });\n })\n\n...initially becomes:\n\n ( 8blk((void) {\n   fptr g = ), 8fn(void, (void), ( 8blk( { 0; } ) )), 8blk(;\n }) )\n\nThis is a flat list of plain code alternating with inner functions. Functions\nnested within inner functions are wrapped in much the same way, using its own\nmain 8blk list, creating a vague tree structure of code block lists within code\nblock lists. Further expansion is paused for the moment by truncating the macro\nnames to begin with a numeral. The enclosing function's name and return type go\ndirectly to the namespace builder macro (where they will provide a root name,\nthen begin to build the header of the emitted block).\n\nFlattening and processing a tree is a fundamentally recursive task, making it\nill-suited to a language where recursion is forbidden! Therefore, the block is\nnext passed to a group of macros implementing a continuation machine. The tasks\nof splitting the block into a list of functions and a list of code + names,\npulling nested functions out and attaching them to the end of the queue, and\nfinding the next task in the queue are handled by this continuation machine.\n\nThe machine itself is heavily ripped off from Vesa Karvonen's Order-PP library,\nexcept much smaller and lighter. A continuation consists of a macro to run, a\nlist of arguments to pass it, and optionally some output to dump (output is put\nat the end, meaning that everything is written in reverse - thus the first code\nencountered, the enclosing namespace using the inner functions, will be last to\nbe written out). The macro is cut off with a numeral and is given a prefix with\nthe paste operator when it is time to run; recursion is not expressed directly,\nbut rather in continuation-passing style.\n\nThe queue of \"next operations\", that accepts nested inner function definitions\nto write (and \"extras\" like the struct definitions for closures) is actually\nnot made explicitly visible to the continuation machine, but instead passed as\npart of the argument list. When a function has been written, it hands control\nover to the 8DO_Q continuation, which grabs the next item from the queue, and\nrepackages the rest into the argument list for it.\n\nBecause a continuation is returned as a parenthesised list of procedure, args,\nand remainder, breaking out is as simple as returning something not contained\nwithin parentheses, so that the next continuation step is not invoked. An \"end\"\nmacro, waiting at the end of the queue, does this in an easily-cleaned-up way.\n\nBecause the next macro to invoke is held in truncated form, the recursion issue\nis bypassed, as no macro returns its own name in full. There is a maximum limit\nto the number of steps the continuation machine can provide, but at 2^N where N\nis the number of defined CM macros, this is very easily extended.\n\nSimpler tasks, such as applying a macro to each element of a list, are handled\nby more traditional macros in \"cmacros.h\". These are not recursion-safe, so are\nonly ever called on to return values by the CM macros. They have a much lower\nmaximum limit, since they grow in a linear fashion (32 by default), but not\nintegrating them into the CM should dramatically improve performance, and also\ndoes not waste CM steps on non-recursive computation. Where the macros need to\nbe able to cull some values (e.g. to avoid calculating the unused branch of an\nIF expression), select additional CM steps can be added to filter the amount of\nwork being demanded.\n\n\nFinal note on usage\n-------------------\n\n...you probably really shouldn't abuse poor innocent C in this way.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlexCeleste%2FC99-Lambda","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAlexCeleste%2FC99-Lambda","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlexCeleste%2FC99-Lambda/lists"}