{"id":13846342,"url":"https://github.com/twhitehead/c99-meta","last_synced_at":"2025-07-12T07:32:31.029Z","repository":{"id":69926242,"uuid":"111011205","full_name":"twhitehead/c99-meta","owner":"twhitehead","description":"Abusing the C99 preprocessor for meta-programming","archived":false,"fork":false,"pushed_at":"2017-11-19T21:55:19.000Z","size":77,"stargazers_count":20,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-02-17T10:35:38.097Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twhitehead.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-11-16T19:08:18.000Z","updated_at":"2024-01-24T16:56:16.000Z","dependencies_parsed_at":"2023-03-03T20:30:20.791Z","dependency_job_id":null,"html_url":"https://github.com/twhitehead/c99-meta","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/twhitehead%2Fc99-meta","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twhitehead%2Fc99-meta/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twhitehead%2Fc99-meta/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twhitehead%2Fc99-meta/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twhitehead","download_url":"https://codeload.github.com/twhitehead/c99-meta/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225807249,"owners_count":17527217,"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.522Z","updated_at":"2024-11-21T21:30:25.206Z","avatar_url":"https://github.com/twhitehead.png","language":"C","funding_links":[],"categories":["Metaprogramming frameworks"],"sub_categories":[],"readme":"# Overview\n\nWhy should C++ people have all the meta-programming fun that comes\nfrom abusing the heck out of their language?\n\nVariadic macros accidentally made the C99 pre-processor [Turing\ncomplete](https://en.wikipedia.org/wiki/Turing_completeness) too.\nIt's time for C people to get in on the fun too!\n\n\n# Quick intro\n\nHave a look at [Tests/List.c](Tests/List.c) to get an idea of how it\nworks.  Compile up this file\n\n```sh\ngcc --std=c99 -E Tests/List.c\n```\n\nand you will see the C99 pre-processor faithfully replaces each\n`eval(...)` line with it's value to match the line below.  Note,\nin particular, the use of partial application in statements like\n\n```C\neval(map,(add,P1),L2(P1,P2))\n```\n\n(i.e., the equivalent to the Haskell `map (+ 1) [1,2]`).\n\nSee [Tests/Goldbach.c](Tests/Goldbach.c) for a more complex example.\n\n# More details\n\nLet's take a quick look at the implementation of `zipWith` in\n[List.h](List.h).  From the [Tests/List.c](Tests/List.c) file we see\n\n```C\n eval(zipWith,(Tupple2),L3(P2,P3,P4),L4(P1,P2,P3,P4))\n={(0x2,0x1),(0x3,0x4),(0x4,0x5)}\n```\nwhere the first line will evaluate to the second (minus the `=`)\nwhen passed through the pre-processor.\n\n## Wonky syntax\n\nThe fact that the pre-processor has very limited syntax and only\nreally understands strings puts some limitations on the syntax.  What\nemerges is a lisp like thing with `,` being the separator instead of\n` `.\n\nThe above could thus be thought of as\n\n```\n(zipWith (Tupple2) L3(P2 P3 P4) L4(P1 P2 P3 P4))\n```\n\nwhere\n\n`PN`\n  ~ the Nth positive integer (i.e., `P2` is `2`)\n\n`LN`\n  ~ a macro to construct a N-component list (i.e., `L3(P2,P3,P4)` is `[2,3,4]`\n\n`TuppleN`\n  ~ the N-component tuple constructor (i.e., `Tupple2` would be `(,)`)\n\n## Implementation\n\nIn Haskell, a `zipWith` implementation would look something like this\n\n```haskell\nzipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys\nzipWith f _      _      = []\n```\n\nOr, in more gruesome detail, if we had to drop the function matching\nsyntactic sugar and fully spelling out all the pattern match cases\n\n```haskell\nzipWith f xs ys = case xs ys of\n  (x:xs) (y:ys) -\u003e f x y : zipWith f xs ys\n  (x:xs) []     -\u003e []\n  []     (y:ys) -\u003e []\n  []     []     -\u003e []\n\n```\n\nLooking into the `List.h` file, we hopefully recognize an implementation\nthat pretty much mirrors this later version\n\n```\n#define _list_zipWith(f,xs,ys,...)                         reduce_caseReduce2(list_zipWith,xs,ys,f,__VA_ARGS__)\n#define _list_zipWith_list_Cons_list_Cons(x,xs,y,ys,f,...) reduce_construct((list_Cons,reduce_apply(f,x,y),(list_zipWith,f,xs,ys)),__VA_ARGS__)\n#define _list_zipWith_list_Cons_list_Nil( x,xs     ,f,...) reduce_construct((list_Nil),__VA_ARGS__)\n#define _list_zipWith_list_Nil_list_Cons(      y,ys,f,...) reduce_construct((list_Nil),__VA_ARGS__)\n#define _list_zipWith_list_Nil_list_Nil(            f,...) reduce_construct((list_Nil),__VA_ARGS__)\n```\n\nThe first line define `_list_zipWith(f,xs,ys,...)`.  It is a `zipWith`\nbinding in the `list` namespace that takes three arguments (note that,\nthis being a lazy system, all definitions are functions).\n\nThe body associated with this binding is a call to the\n`reduce_caseReduce2` macro (the leading `reduce` is a namespace -- see\n[Reduce.h](Reduce.h) for full details and more forms).  This macro takes the\nbase name of the next macro to call, two arguments to evaluate to weak\nhead normal form (WHNF) whose types are append to the base name, and\nany other arguments/bindings to pass along.\n\nThe rest is then just bindings for each of the cases.  Each of them\nconstruct a list type by calling the macro `reduce_construct` (again\nthe leading `reduce` is just the namesapce -- see [Reduce.h](Reduce.h)\nfor full details).  This constructs the type an continues with\nwhatever caused us to be evaluated.\n\nIn the first case it is a `list_Cons` constructor (i.e., `:` in\nHaskell), where the first argument (the head of the list) is `f`\napplied to `x` and `y` and the second argument (the tail of the list)\nis a recursive call to zipWith.  In the remainder cases, as with the\nHaskell code, it is just the `list_Nil` constructor.\n\n\n# Debugging\n\nThe evaluation should not go astray if the types are correct (i.e.,\nfunctions cover all the cases for the types they support and are only\napplied to arguments of those types).  Noticeably absent though is\ntype annotations and a type checker to ensure this is the case.\n\nWhat's the nest best thing?  Break the code into small bits and tests\nto ensure those small bits do what they are suppose to do.  Another\neasy option is to translate the problematic code to Haskell.  Haskell\nhas a type checker, so it will tell you where things went wrong.\n\n## Decoding bad output\n\nAll that aside, there is some basic information to be obtained from\nthe bad output.  Take, for example, the incorrect expression\n\n```C\neval(zipWith,(Tuple2),L3(P2,P3,P4),P4)\n```\n\nwhich calls `zipWith` with a `Integer` third argument instead of a\n`List`.  It evaluates to a long ugly string that begins with\n\n```\n_list_zipWith_list_Cons_integer_Integer\n```\n\nprefixed with a bunch of `_recurse_NN_` prefixes.  What this means is\npattern matching went astray because in the definition of `zipWith`\nabove\n\n```C\nreduce_caseReduce2(list_zipWith,xs,ys,f,__VA_ARGS__)\n```\n\nthere was no `list_zipWith_list_Cons_integer_Integer` branch.  The\n`ys` argument must be of `List` type because it is only matched\nagainst the `List` constructors.  An invalid call was made to\n`zipWith` with an incorrect third argument that was not a `List`.\n\n## Single stepping\n\nWhere?  Well that gets messy.  The evaluation can be stopped at an\narbitrary step by defining `RECURSE_DEBUG`.  For example,\n\n```sh\ngcc --std=c99 -E -D \"RECURSE_DEBUG=(0,0,1,c)\" Tests/List.c\n```\n\nstops evaluation at recursion step `0x1c` (i.e., `28`).  With a bit of\nshell scripting to repeatedly call it for incrementing values of\n`RECURSE_DEBUG` it is possible to simulate single stepping the\nevaluation.\n\nFor example, assume the problematic code is in the file\n[Debug.c](Debug.c).\n\n```\n#include \"List.h\"\n#include \"Integer.h\"\n\ntag: eval(zipWith,(Tuple2),L3(P2,P3,P4),P3)\n```\n\nwhere we've added `tag:` as some arbitrary bit of text filter the\noutput to just the line we are interested in.  Then the following code\nwill dump a trace of the first 256 evaluation steps\n\n```sh\nfor i1 in {0..9} a b c d e f; do\n  for i0 in in {0..9} a b c d e f; do\n    gcc --std=c99 -E -D \"RECURSE_DEBUG=(0,0,$i1,$i0)\" Example.c | sed -e '/^tag:/!d' \u003e\u003e Example.c-singlestep\n  done\ndone\n```\n\nOf course the evaluation is lazy, and single stepping lazy programs\ngives notoriously convoluted code path.\n\nWhat to do?  Did we mention how it is a good idea to break the program\ninto small bits and verify that these small bits work as advertised\nbefore attempting building bigger components from faulty blocks?\n\n\n# Implementation\n\nThe code is well documented.  See [Recurse.h](Recurse.h) for details\nabout how Variadic macros accidentally made the the C99 pre-processor\nturning complete.  Then see [Reduce.h](Reduce.h) for details on how a\nsomewhat Haskell-like syntax is achieved on top of this.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwhitehead%2Fc99-meta","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwhitehead%2Fc99-meta","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwhitehead%2Fc99-meta/lists"}