{"id":13846361,"url":"https://github.com/swansontec/map-macro","last_synced_at":"2025-04-09T23:18:59.683Z","repository":{"id":5559045,"uuid":"6763898","full_name":"swansontec/map-macro","owner":"swansontec","description":"A recursive C preprocessor macro which performs an operation on each element of a list","archived":false,"fork":false,"pushed_at":"2021-08-10T20:28:25.000Z","size":9,"stargazers_count":288,"open_issues_count":7,"forks_count":24,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-04-14T07:20:15.414Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swansontec.png","metadata":{"files":{"readme":"README.md","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-11-19T17:02:19.000Z","updated_at":"2024-04-12T18:56:26.000Z","dependencies_parsed_at":"2022-07-21T11:29:05.057Z","dependency_job_id":null,"html_url":"https://github.com/swansontec/map-macro","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/swansontec%2Fmap-macro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swansontec%2Fmap-macro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swansontec%2Fmap-macro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swansontec%2Fmap-macro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swansontec","download_url":"https://codeload.github.com/swansontec/map-macro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125644,"owners_count":21051777,"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:31.012Z","updated_at":"2025-04-09T23:18:59.663Z","avatar_url":"https://github.com/swansontec.png","language":"C","funding_links":[],"categories":["Other projects"],"sub_categories":[],"readme":"# The Map Macro\n\nThis repository implements a `MAP` macro, which can be used as follows:\n\n```c\n#define PRINT(a) printf(#a\": %d\", a); /* An example macro */\nMAP(PRINT, a, b, c) /* Apply PRINT to a, b, and c */\n```\n\nThis macro came about in answer to a [Stack Overflow question.](http://stackoverflow.com/questions/6707148/foreach-macro-on-macros-arguments/13459454#13459454).\nThe original answer can be found in the [`stackoverflow` branch](https://github.com/swansontec/map-macro/tree/stackoverflow).\nThe `master` branch contains a few extra enhancements.\nPull requests are welcome.\n\n## How it Works\n\nThe goal is to create a macro which performs some operation each element of\na list. Doing that requires recursion, though, which the C preprocessor\ndoesn't allow. Fortunately, there is a workaround.\n\n### Basic Recursion\n\nFirst, we need a technique for emitting something that looks like a macro\ncall, but isn't yet:\n\n    #define MAP_OUT\n\nImagine we have the following macros:\n\n    #define A(x) x B MAP_OUT (x)\n    #define B(x) x A MAP_OUT (x)\n\nEvaluating the macro `A (blah)` produces the output text:\n\n    blah B (blah)\n\nThe preprocessor doesn't see any recursion, since the `B (blah)` call is\njust plain text at this point, and `B` isn't even the name of the current\nmacro. Feeding this text back into the preprocessor expands the call,\nproducing the output:\n\n    blah blah A (blah)\n\nEvaluating the output a third time expands the `A (blah)` macro, carrying\nthe recursion full-circle. The recursion continues as long as the caller\ncontinues to feed the output text back into the preprocessor.\n\nTo perform these repeated evaluations, the following `EVAL` macro passes\nits arguments down a tree of macro calls:\n\n    #define EVAL0(...) __VA_ARGS__\n    #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__)))\n    #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__)))\n    #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__)))\n    #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__)))\n    #define EVAL(...)  EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))\n\nEach level multiplies the effort of the level before, evaluating the input\n365 times in total. In other words, calling `EVAL (A (blah))` would\nproduce 365 copies of the word `blah`, followed by a final un-evaluated `B\n(blah)`. This provides the basic framework for recursion, at least within a\ncertain stack depth.\n\n### End Detection\n\nThe next challenge is to stop the recursion when it reaches the end of the\nlist.\n\nThe basic idea is to emit the following macro name instead of the normal\nrecursive macro when the time comes to quit:\n\n    #define MAP_END(...)\n\nEvaluating this macro does nothing, which ends the recursion.\n\nTo actually select between the two macros, the following `MAP_NEXT`\nmacro compares a single list item against the special end-of-list marker\n`()`. The macro returns `MAP_END` if the test item matches, or the `next`\nparameter if the item is anything else:\n\n    #define MAP_GET_END() 0, MAP_END\n    #define MAP_NEXT0(test, next, ...) next MAP_OUT\n    #define MAP_NEXT1(test, next) MAP_NEXT0 (test, next, 0)\n    #define MAP_NEXT(test, next)  MAP_NEXT1 (MAP_GET_END test, next)\n\nThis macro works by placing the test item next to the `MAP_GET_END` macro.\nIf doing that forms a macro call, everything moves over by a slot in the\n`MAP_NEXT0` parameter list, changing the output. The `MAP_OUT` trick\nprevents the preprocessor from evaluating the final result.\n\n### Putting it All Together\n\nWith these pieces in place, it is now possible to implement useful versions\nof the `A` and `B` macros from the example above:\n\n    #define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__)\n    #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)\n\nThese macros apply the operation `f` to the current list item `x`. They then\nexamine the next list item, `peek`, to see if they should continue or not.\n\nThe final step is to tie everything together in a top-level `MAP` macro:\n\n    #define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))\n\nThis macro places a `()` marker on the end of the list, as well as an extra\n`0` for ANSI compliance (otherwise, the last iteration would have an illegal\n0-length list). It then passes the whole thing through `EVAL` and\nreturns the result.\n\n### Evaluation Depth\n\nEach level of the `EVAL` macro multiplies the effort of the previous\nlevel by 3, but also adds one evaluation of its own. Invoking the macro as a\nwhole adds one more level, taking the total to:\n\n    1 + (3 * (3 * (3 * (3 * (3 * (1) + 1) + 1) + 1) + 1) + 1) = 365\n\nOther interesting combinations include:\n\n    calls/level levels  total depth\n    2           3       16\n    2           4       32\n    2           5       64\n    3           3       41\n    3           4       122\n    3           5       365\n    4           3       86\n    4           4       342\n    4           5       1366\n    5           2       32\n    5           3       157\n    5           4       782\n\nSpecial thanks to [pfultz2](https://github.com/pfultz2/Cloak/wiki/Is-the-C-preprocessor-Turing-complete%3F) for inventing the EVAL idea.\n\n## See Also\n\nC++ users may be intersted in the ['visit_struct' library](https://github.com/cbeck88/visit_struct),\nwhich uses a version of this macro to implement structure visitors in C++11.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswansontec%2Fmap-macro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswansontec%2Fmap-macro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswansontec%2Fmap-macro/lists"}