metac 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. metac-0.0.1/LICENSE +26 -0
  2. metac-0.0.1/PKG-INFO +380 -0
  3. metac-0.0.1/README.md +361 -0
  4. metac-0.0.1/pyproject.toml +34 -0
  5. metac-0.0.1/setup.cfg +4 -0
  6. metac-0.0.1/src/metac/__init__.py +6 -0
  7. metac-0.0.1/src/metac/array.py +402 -0
  8. metac-0.0.1/src/metac/assignable.py +26 -0
  9. metac-0.0.1/src/metac/autotest/__init__.py +298 -0
  10. metac-0.0.1/src/metac/autotest/__main__.py +11 -0
  11. metac-0.0.1/src/metac/autotest/concepts.py +772 -0
  12. metac-0.0.1/src/metac/autotest/elements.py +241 -0
  13. metac-0.0.1/src/metac/autotest/matrix.py +150 -0
  14. metac-0.0.1/src/metac/autotest/template/@module@.py +10 -0
  15. metac-0.0.1/src/metac/avl_set.py +703 -0
  16. metac-0.0.1/src/metac/bisectable.py +86 -0
  17. metac-0.0.1/src/metac/bit_array.py +440 -0
  18. metac-0.0.1/src/metac/bit_vector.py +940 -0
  19. metac-0.0.1/src/metac/btree_map.py +151 -0
  20. metac-0.0.1/src/metac/btree_set.py +734 -0
  21. metac-0.0.1/src/metac/chained_hash_map.py +108 -0
  22. metac-0.0.1/src/metac/chained_hash_set.py +365 -0
  23. metac-0.0.1/src/metac/circular_buffer.py +802 -0
  24. metac-0.0.1/src/metac/cmake/__init__.py +75 -0
  25. metac-0.0.1/src/metac/cmake/__main__.py +12 -0
  26. metac-0.0.1/src/metac/container.py +119 -0
  27. metac-0.0.1/src/metac/core.py +1489 -0
  28. metac-0.0.1/src/metac/counter.py +712 -0
  29. metac-0.0.1/src/metac/deque.py +440 -0
  30. metac-0.0.1/src/metac/doc/__init__.py +73 -0
  31. metac-0.0.1/src/metac/doc/__main__.py +11 -0
  32. metac-0.0.1/src/metac/doc/catalog.py +387 -0
  33. metac-0.0.1/src/metac/doc/containers.md +118 -0
  34. metac-0.0.1/src/metac/doc/design.md +174 -0
  35. metac-0.0.1/src/metac/doc/mainpage.md +137 -0
  36. metac-0.0.1/src/metac/doc/template/.gitignore +12 -0
  37. metac-0.0.1/src/metac/doc/template/@module@.py +17 -0
  38. metac-0.0.1/src/metac/doc/template/Doxyfile +21 -0
  39. metac-0.0.1/src/metac/doc/template/catalog.py +1 -0
  40. metac-0.0.1/src/metac/doc/tutorial.md +166 -0
  41. metac-0.0.1/src/metac/flat_map.py +157 -0
  42. metac-0.0.1/src/metac/flat_multimap.py +394 -0
  43. metac-0.0.1/src/metac/flat_multiset.py +100 -0
  44. metac-0.0.1/src/metac/flat_set.py +58 -0
  45. metac-0.0.1/src/metac/flat_sets.py +459 -0
  46. metac-0.0.1/src/metac/hash.py +48 -0
  47. metac-0.0.1/src/metac/hashed.py +33 -0
  48. metac-0.0.1/src/metac/indexable.py +65 -0
  49. metac-0.0.1/src/metac/insertable.py +36 -0
  50. metac-0.0.1/src/metac/intrusive_hash_map.py +155 -0
  51. metac-0.0.1/src/metac/intrusive_hash_set.py +409 -0
  52. metac-0.0.1/src/metac/intrusive_option.py +80 -0
  53. metac-0.0.1/src/metac/list.py +309 -0
  54. metac-0.0.1/src/metac/mapping.py +460 -0
  55. metac-0.0.1/src/metac/memory.py +60 -0
  56. metac-0.0.1/src/metac/meson/__init__.py +63 -0
  57. metac-0.0.1/src/metac/meson/__main__.py +12 -0
  58. metac-0.0.1/src/metac/module.py +506 -0
  59. metac-0.0.1/src/metac/multimap.py +566 -0
  60. metac-0.0.1/src/metac/multimapping.py +97 -0
  61. metac-0.0.1/src/metac/multiset.py +386 -0
  62. metac-0.0.1/src/metac/option.py +51 -0
  63. metac-0.0.1/src/metac/options.py +326 -0
  64. metac-0.0.1/src/metac/ordered.py +37 -0
  65. metac-0.0.1/src/metac/packet.py +536 -0
  66. metac-0.0.1/src/metac/priority_queue.py +299 -0
  67. metac-0.0.1/src/metac/project/__init__.py +63 -0
  68. metac-0.0.1/src/metac/project/__main__.py +11 -0
  69. metac-0.0.1/src/metac/project/cmake/__init__.py +71 -0
  70. metac-0.0.1/src/metac/project/cmake/__main__.py +10 -0
  71. metac-0.0.1/src/metac/project/cmake/template/.gitignore +3 -0
  72. metac-0.0.1/src/metac/project/cmake/template/.vscode/launch.json +13 -0
  73. metac-0.0.1/src/metac/project/cmake/template/@project@.code-workspace +12 -0
  74. metac-0.0.1/src/metac/project/cmake/template/CMakeLists.txt +45 -0
  75. metac-0.0.1/src/metac/project/cmake/template/CMakePresets.json +206 -0
  76. metac-0.0.1/src/metac/project/cmake/template/cmake/MetaC.cmake +121 -0
  77. metac-0.0.1/src/metac/project/meson/__init__.py +70 -0
  78. metac-0.0.1/src/metac/project/meson/__main__.py +10 -0
  79. metac-0.0.1/src/metac/project/meson/template/.gitignore +4 -0
  80. metac-0.0.1/src/metac/project/meson/template/.vscode/launch.json +13 -0
  81. metac-0.0.1/src/metac/project/meson/template/@project@.code-workspace +12 -0
  82. metac-0.0.1/src/metac/project/meson/template/meson.build +110 -0
  83. metac-0.0.1/src/metac/project/meson/template/meson.options +5 -0
  84. metac-0.0.1/src/metac/project/scons/__init__.py +71 -0
  85. metac-0.0.1/src/metac/project/scons/__main__.py +10 -0
  86. metac-0.0.1/src/metac/project/scons/template/.gitignore +11 -0
  87. metac-0.0.1/src/metac/project/scons/template/.vscode/launch.json +14 -0
  88. metac-0.0.1/src/metac/project/scons/template/.vscode/tasks.json +28 -0
  89. metac-0.0.1/src/metac/project/scons/template/@project@.code-workspace +12 -0
  90. metac-0.0.1/src/metac/project/scons/template/SConstruct +55 -0
  91. metac-0.0.1/src/metac/project/scons/template/site_scons/site_tools/metac.py +91 -0
  92. metac-0.0.1/src/metac/properties.py +11 -0
  93. metac-0.0.1/src/metac/queue.py +255 -0
  94. metac-0.0.1/src/metac/random.py +176 -0
  95. metac-0.0.1/src/metac/range.py +184 -0
  96. metac-0.0.1/src/metac/rb_set.py +630 -0
  97. metac-0.0.1/src/metac/record.py +205 -0
  98. metac-0.0.1/src/metac/reference.py +216 -0
  99. metac-0.0.1/src/metac/sample/__init__.py +41 -0
  100. metac-0.0.1/src/metac/sample/__main__.py +12 -0
  101. metac-0.0.1/src/metac/sample/template/@module@.c +10 -0
  102. metac-0.0.1/src/metac/sample/template/@module@.py +15 -0
  103. metac-0.0.1/src/metac/scaffolder.py +56 -0
  104. metac-0.0.1/src/metac/scons/__init__.py +159 -0
  105. metac-0.0.1/src/metac/scons/__main__.py +12 -0
  106. metac-0.0.1/src/metac/sequential.py +35 -0
  107. metac-0.0.1/src/metac/set.py +341 -0
  108. metac-0.0.1/src/metac/sortable.py +127 -0
  109. metac-0.0.1/src/metac/stack.py +240 -0
  110. metac-0.0.1/src/metac/std.py +118 -0
  111. metac-0.0.1/src/metac/string.py +385 -0
  112. metac-0.0.1/src/metac/string_buffer.py +567 -0
  113. metac-0.0.1/src/metac/test/__init__.py +269 -0
  114. metac-0.0.1/src/metac/test/__main__.py +11 -0
  115. metac-0.0.1/src/metac/test/avl_int2int_map.py +160 -0
  116. metac-0.0.1/src/metac/test/btree_int2int_map.py +198 -0
  117. metac-0.0.1/src/metac/test/chained_cstring_hash_set.py +147 -0
  118. metac-0.0.1/src/metac/test/chained_cstring_int_hash_map.py +118 -0
  119. metac-0.0.1/src/metac/test/chained_functional_hash_set.py +100 -0
  120. metac-0.0.1/src/metac/test/chained_int2int_hash_map.py +154 -0
  121. metac-0.0.1/src/metac/test/chained_int_hash_set.py +268 -0
  122. metac-0.0.1/src/metac/test/composite_packet.py +107 -0
  123. metac-0.0.1/src/metac/test/content_variant.py +135 -0
  124. metac-0.0.1/src/metac/test/cstring.py +7 -0
  125. metac-0.0.1/src/metac/test/cstring_circular_buffer.py +172 -0
  126. metac-0.0.1/src/metac/test/cstring_deque.py +259 -0
  127. metac-0.0.1/src/metac/test/cstring_flat_multiset.py +46 -0
  128. metac-0.0.1/src/metac/test/cstring_priority_queue.py +48 -0
  129. metac-0.0.1/src/metac/test/cstring_queue.py +212 -0
  130. metac-0.0.1/src/metac/test/cstring_tiered_vector.py +65 -0
  131. metac-0.0.1/src/metac/test/cstring_vector.py +101 -0
  132. metac-0.0.1/src/metac/test/custom_composite.py +82 -0
  133. metac-0.0.1/src/metac/test/custom_composite_arc.py +28 -0
  134. metac-0.0.1/src/metac/test/derived_move.py +53 -0
  135. metac-0.0.1/src/metac/test/flat_cstring_int_map.py +82 -0
  136. metac-0.0.1/src/metac/test/flat_cstring_int_multimap.py +121 -0
  137. metac-0.0.1/src/metac/test/flat_int2int_map.py +184 -0
  138. metac-0.0.1/src/metac/test/flat_int2int_multimap.py +242 -0
  139. metac-0.0.1/src/metac/test/functional.py +132 -0
  140. metac-0.0.1/src/metac/test/functional_vector.py +89 -0
  141. metac-0.0.1/src/metac/test/int_array.py +269 -0
  142. metac-0.0.1/src/metac/test/int_avl_set.py +329 -0
  143. metac-0.0.1/src/metac/test/int_btree_set.py +329 -0
  144. metac-0.0.1/src/metac/test/int_circular_buffer.py +457 -0
  145. metac-0.0.1/src/metac/test/int_deque.py +302 -0
  146. metac-0.0.1/src/metac/test/int_flat_multiset.py +252 -0
  147. metac-0.0.1/src/metac/test/int_flat_set.py +294 -0
  148. metac-0.0.1/src/metac/test/int_inlined_vector.py +377 -0
  149. metac-0.0.1/src/metac/test/int_intrusive_option.py +168 -0
  150. metac-0.0.1/src/metac/test/int_list.py +266 -0
  151. metac-0.0.1/src/metac/test/int_option.py +189 -0
  152. metac-0.0.1/src/metac/test/int_packet.py +228 -0
  153. metac-0.0.1/src/metac/test/int_priority_queue.py +113 -0
  154. metac-0.0.1/src/metac/test/int_queue.py +274 -0
  155. metac-0.0.1/src/metac/test/int_rb_set.py +329 -0
  156. metac-0.0.1/src/metac/test/int_stack.py +267 -0
  157. metac-0.0.1/src/metac/test/int_tiered_vector.py +309 -0
  158. metac-0.0.1/src/metac/test/int_treap_set.py +362 -0
  159. metac-0.0.1/src/metac/test/int_vector.py +306 -0
  160. metac-0.0.1/src/metac/test/intrusive_cstring_hash_set.py +85 -0
  161. metac-0.0.1/src/metac/test/intrusive_custom_composite_arc_hash_set.py +86 -0
  162. metac-0.0.1/src/metac/test/intrusive_int2int_hash_map.py +102 -0
  163. metac-0.0.1/src/metac/test/intrusive_int_hash_set.py +233 -0
  164. metac-0.0.1/src/metac/test/intrusive_primitive_arc_hash_set.py +86 -0
  165. metac-0.0.1/src/metac/test/minimal_composite.py +49 -0
  166. metac-0.0.1/src/metac/test/primitive_arc.py +28 -0
  167. metac-0.0.1/src/metac/test/primitive_raw.py +27 -0
  168. metac-0.0.1/src/metac/test/primitive_record.py +41 -0
  169. metac-0.0.1/src/metac/test/rb_int2int_map.py +177 -0
  170. metac-0.0.1/src/metac/test/rb_vector_multimap.py +228 -0
  171. metac-0.0.1/src/metac/test/string_buffer.py +136 -0
  172. metac-0.0.1/src/metac/test/template/@module@.py +10 -0
  173. metac-0.0.1/src/metac/test/test_bit_array.py +309 -0
  174. metac-0.0.1/src/metac/test/test_bit_vector.py +389 -0
  175. metac-0.0.1/src/metac/test/test_counter.py +416 -0
  176. metac-0.0.1/src/metac/test/test_emplacement.py +292 -0
  177. metac-0.0.1/src/metac/test/test_module.py +190 -0
  178. metac-0.0.1/src/metac/test/test_optional.py +437 -0
  179. metac-0.0.1/src/metac/test/test_string.py +83 -0
  180. metac-0.0.1/src/metac/test/test_traits.py +552 -0
  181. metac-0.0.1/src/metac/test/treap_cstring_int_hash_map.py +118 -0
  182. metac-0.0.1/src/metac/test/treap_int2int_hash_map.py +160 -0
  183. metac-0.0.1/src/metac/tiered_vector.py +468 -0
  184. metac-0.0.1/src/metac/traversable.py +43 -0
  185. metac-0.0.1/src/metac/treap_set.py +764 -0
  186. metac-0.0.1/src/metac/tree_map.py +110 -0
  187. metac-0.0.1/src/metac/variant.py +231 -0
  188. metac-0.0.1/src/metac/vector.py +629 -0
  189. metac-0.0.1/src/metac.egg-info/PKG-INFO +380 -0
  190. metac-0.0.1/src/metac.egg-info/SOURCES.txt +190 -0
  191. metac-0.0.1/src/metac.egg-info/dependency_links.txt +1 -0
  192. metac-0.0.1/src/metac.egg-info/top_level.txt +1 -0
metac-0.0.1/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2013-2026, Oleg A. Khlybov <fougas@mail.ru>
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this
7
+ list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ 3. Neither the name of the copyright holder nor the names of its
14
+ contributors may be used to endorse or promote products derived from
15
+ this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
metac-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,380 @@
1
+ Metadata-Version: 2.4
2
+ Name: metac
3
+ Version: 0.0.1
4
+ Summary: C metaprogramming & code generation package
5
+ Author-email: "Oleg A. Khlybov" <fougas@mail.ru>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Homepage, https://github.com/okhlybov/metac
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: C
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Software Development :: Code Generators
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Dynamic: license-file
19
+
20
+ # metac: C metaprogramming & code generation package for Python
21
+
22
+ <div align="right"><i>/* Automagically generated by metac $your_favorite_version */</i></div><br><br>
23
+
24
+ `metac` is a general-purpose template engine targeting plain ANSI C — simply put, it is to C what templates are to C++. Generic types are written once in Python against type parameters; the engine specializes each definition for the concrete type arguments a module actually uses and emits ordinary C source files. Using the same monomorphization technique a C++ compiler performs, every (definition, type arguments) pair is stamped out at generation time as a distinct, fully typed implementation. Nothing generic survives into compilation: the C compiler only ever sees plain, C++-safe ANSI C code.
25
+
26
+ Alongside the template engine, `metac` provides a comprehensive standard library of containers and utility types — a functional analog of the C++ STL. It spans direct-access and linked sequences, strings and string buffers, hashed, intrusive, and ordered sets and maps, bit sets, records, tagged variants, and raw or reference-counted references. Custom types are first-class citizens: implement the value protocol in Python for complex custom types to work seamlessly as elements and fields anywhere in the catalog, intrusive containers included.
27
+
28
+ What holds everything together is uniform value semantics with the uniform treatment of primitive and composite types. Every type — from int to a nested map of records — carries structured lifetime and maintenance operations, with capabilities derived automatically from its constituent parts and only valid operations emitted. Primitives realize the protocol as inline expressions while composites use generated extern or inline functions, keeping call sites uniform. Owing to free composition of build-in and custom types a list of vectors of records is just another type definition. Ever dreamt of having `std::map<std::string, std::list<int>>` in plain C? So here it is.
29
+
30
+ ## Why metac?
31
+
32
+ C is notorious for its complete lack of complex data types in the standard library, and the usual workarounds each hurt in a familiar way:
33
+
34
+ - Macro-based generics (typeof tricks, token pasting) are opaque, hard to debug, and explode into incomprehensible errors.
35
+
36
+ - `void*`-based generic libraries abandon type safety, incur pointer-chasing overhead, and obscure ownership semantics (who copies? who destroys?).
37
+
38
+ - Hand-writing one container per type works, but is tedious, error-prone, and never quite finished.
39
+
40
+ `metac` takes a fourth road: containers are written once against a strict value-semantics protocol, and the generator stamps out the exact C code that would have otherwise written manually (arguably better), with none of the macro or `void*` compromises.
41
+
42
+ - **Zero dependencies:** generated code targets strict ANSI C and requires nothing beyond the C standard library — just #include the header and compile the generated sources along with consuming code.
43
+
44
+ - **Build-system native:** provides CMake, SCons, and Meson integrations that automatically re-run the generator whenever definitions change, so specialized code never goes stale.
45
+
46
+ - **Battle-tested output:** the project validates its generated test suites across MSVC, GCC, Clang, along with a bunch of second tier C compilers.
47
+
48
+ - **Bolierplate control:** the generator is designed to deal with large amount of generated C code with its ability to distribute the code over multiple translation units to sport parallel compilation and to perform the smart generation to avoid triggering unneccessary recompilations of unaltered code.
49
+
50
+ __This project is the successor to the [AutoC](https://github.com/okhlybov/autoc) package for Ruby.__
51
+
52
+ ## Quick start
53
+
54
+ Requires Python ≥ 3.10. `metac` itself has zero dependencies.
55
+
56
+ **1. Define a module** — e.g. `mymodule.py`:
57
+
58
+ ```python
59
+ from metac.module import Module
60
+ from metac.vector import Vector
61
+ from metac.chained_hash_map import Map
62
+ import metac.std as std
63
+
64
+ with Module("mymodule") as m:
65
+ m.add(Vector("int_vector", "int"))
66
+ m.add(Map("str2double", "const char*", "double"))
67
+ ```
68
+
69
+ **2. Generate** — run the script; `metac` writes `mymodule_auto.h` / `mymodule_auto.c` (splitting into several sources when a module grows too large for one translation unit).
70
+
71
+ **3. Use** — from C:
72
+
73
+ ```c
74
+ #include "mymodule_auto.h"
75
+
76
+ int_vector v;
77
+ int_vector_create(&v);
78
+ int_vector_set(&v, 0, 42);
79
+ assert(int_vector_get(&v, 0) == 42);
80
+
81
+ /* iterate */
82
+ int_vector_range r;
83
+ for(r = int_vector_range_new(&v); !int_vector_range_empty(&r);
84
+ int_vector_range_move_front(&r)) {
85
+ printf("%d\n", int_vector_range_front(&r));
86
+ }
87
+
88
+ int_vector_destroy(&v);
89
+ ```
90
+
91
+ Every generated type speaks the same vocabulary of value semantics:
92
+
93
+ ```c
94
+ void T_create (T* target); /* default construction */
95
+ void T_destroy(T* target); /* release resources */
96
+ void T_copy (T* target, const T* src);
97
+ int T_equal (const T* left, const T* right);
98
+ int T_compare(const T* left, const T* right); /* when orderable */
99
+ size_t T_hash (const T* target); /* when hashable */
100
+ ```
101
+
102
+ plus the container-specific operations (`set`/`get`/`view`/`contains` for sequences and sets, `put`/`remove` for sets, `get`/`set`/`view` for maps, `empty`/`size`, forward ranges for iteration, …).
103
+
104
+ ## Tutorial: a complete project, end to end
105
+
106
+ Let's build a small but real program — a sensor log that accumulates readings into a **list of ints**, aggregates them and drains them — from an empty directory to a running binary.
107
+
108
+ ### 1. Install the generator
109
+
110
+ From a checkout of this repository (Python ≥ 3.10 required):
111
+
112
+ ```sh
113
+ pip install .
114
+ ```
115
+
116
+ or as a package from remote PyPi repository:
117
+
118
+ ```sh
119
+ pip install metac
120
+ ```
121
+
122
+ ### 2. Scaffold the project
123
+
124
+ `metac` provides project scaffolders for CMake, SCons, and Meson:
125
+
126
+ ```sh
127
+ python -m metac.cmake readings # CMake
128
+ # or: python -m metac.scons readings # SCons
129
+ # or: python -m metac.meson readings # Meson
130
+ # or: python -m metac.sample readings # generic, defaults to CMake (-b meson / -b scons)
131
+ cd readings
132
+ ```
133
+
134
+ This sets up a working project with the generator already wired in. For CMake:
135
+
136
+ ```
137
+ readings/
138
+ ├── CMakeLists.txt # build + regeneration rules
139
+ ├── CMakePresets.json # debug/release presets
140
+ ├── cmake/
141
+ │ └── MetaC.cmake # add_metac_module() helper
142
+ ├── readings.py # type definitions — the generator script
143
+ ├── readings.c # your application
144
+ ├── readings.code-workspace
145
+ ├── .vscode/launch.json
146
+ └── .gitignore
147
+ ```
148
+
149
+ For SCons, the scaffolder generates `SConstruct`, `site_scons/site_tools/metac.py`, and `.metac/scons.site`.
150
+ For Meson, it generates `meson.build`, `meson.options`, and `.metac/meson.site`.
151
+
152
+ The project name doubles as the module name: `readings.py` will generate `readings_auto.h` / `readings_auto.c`, and CMake will build them as the `readings-auto` library your executable links against.
153
+
154
+ ### 3. Define the container
155
+
156
+ Replace `readings.py` with:
157
+
158
+ ```python
159
+ import metac.core
160
+ import metac.module
161
+ import metac.cmake
162
+ from metac.list import List
163
+
164
+ # CamelCase naming (IntListPushFront) is the default; switch to the
165
+ # snake_case style used throughout the test suite and this document:
166
+ metac.core.decorator = metac.core.snake_decorator
167
+
168
+ with metac.module.Module("readings") as m:
169
+ m.add(List("int_list", "int"))
170
+
171
+ metac.cmake.CMake(m) # emit the CMake fragment wiring the generated sources
172
+ ```
173
+
174
+ ### 4. Use it from C
175
+
176
+ Replace `readings.c` with:
177
+
178
+ ```c
179
+ #include <stdio.h>
180
+ #include "readings_auto.h"
181
+
182
+ int main(void) {
183
+ int_list readings;
184
+ int_list_create(&readings);
185
+
186
+ /* readings arrive one at a time, newest first */
187
+ int samples[] = {23, 21, 25, 24, 27, 22, 26};
188
+ for(size_t i = 0; i < sizeof(samples) / sizeof(samples[0]); ++i) {
189
+ int_list_push_front(&readings, samples[i]);
190
+ }
191
+
192
+ /* aggregate via the range interface */
193
+ size_t count = int_list_size(&readings);
194
+ long sum = 0;
195
+ int max = 0;
196
+ for(int_list_range r = int_list_range_new(&readings);
197
+ !int_list_range_empty(&r);
198
+ int_list_range_move_front(&r)) {
199
+ int v = int_list_range_front(&r);
200
+ sum += v;
201
+ if(v > max) max = v;
202
+ }
203
+ printf("count=%zu sum=%ld avg=%.1f max=%d\n",
204
+ count, sum, (double)sum / (double)count, max);
205
+
206
+ /* membership test */
207
+ printf("seen 25: %s\n", int_list_contains(&readings, 25) ? "yes" : "no");
208
+
209
+ /* drain, consuming the stored values one by one */
210
+ while(!int_list_empty(&readings)) {
211
+ printf("consumed %d\n", int_list_pop_front(&readings));
212
+ }
213
+
214
+ int_list_destroy(&readings);
215
+ return 0;
216
+ }
217
+ ```
218
+
219
+ ### 5. Build and run
220
+
221
+ With CMake:
222
+
223
+ ```sh
224
+ cmake --preset debug
225
+ cmake --build --preset debug
226
+ ./build/debug/readings
227
+ ```
228
+
229
+ With SCons:
230
+
231
+ ```sh
232
+ scons
233
+ ./readings
234
+ ```
235
+
236
+ With Meson:
237
+
238
+ ```sh
239
+ meson setup build
240
+ meson compile -C build
241
+ ./build/readings
242
+ ```
243
+
244
+ Output:
245
+
246
+ ```
247
+ count=7 sum=168 avg=24.0 max=27
248
+ seen 25: yes
249
+ consumed 26
250
+ consumed 22
251
+ consumed 27
252
+ consumed 24
253
+ consumed 25
254
+ consumed 21
255
+ consumed 23
256
+ ```
257
+
258
+ (The values come back in reverse order of insertion — a singly-linked list is pushed and popped at the front.)
259
+
260
+ ### What just happened
261
+
262
+ - Running `readings.py` rendered `readings_auto.h` / `readings_auto.c` — plain C, no runtime — plus `readings.cmake` and `readings.state` (digests of what was generated).
263
+ - `add_metac_module()` in `CMakeLists.txt` registered the generator as a proper build step: it re-runs `readings.py` whenever the definitions change, so generated code never goes stale.
264
+ - `readings-auto` is an object library; your executable just links it and includes the header.
265
+ - The workflow from here: add more types to the module (a `Map` for calibration constants, an intrusive hash set for deduplication, a `Record` for readings with timestamps, …), rerun the build, and use the generated functions — nothing else in your C code changes.
266
+
267
+ ## Principles
268
+
269
+ **Plain C, zero runtime.** The deliverable is source code you can read, grep, step through in a debugger and ship. No library to link, no ABI to track, no preprocessor machinery to fight. Regenerating is cheap; the code is meant to be regenerated, not edited.
270
+
271
+ **Type-safe by monomorphization.** Each container is specialized to its element type at generation time. No `void*`, no casts, no element-size parameters — `int_vector_set` takes an `int`, `str2double_set` takes a `const char*` and a `double`. Passing conventions are chosen per element kind: primitives cross API boundaries by value, composites by pointer-to-const.
272
+
273
+ **One protocol for all types.** Primitives and composites are not two worlds. Every type — from `int` to a nested `Map` of records — carries the same operations (`create`/`destroy`/`copy`/`equal`/`compare`/`hash`), so containers compose freely: a `List` of `Vector`s of records just works. Primitives realize the protocol as inline expressions (`target = source`, `left == right`, a cast for hashing); composites realize it as generated functions. The difference never leaks to the call sites.
274
+
275
+ **Capabilities are derived, dead code is elided.** Types carry traits — constructible, destructible, copyable, comparable, orderable, hashable. Composites derive them structurally from their fields (a record is destructible if any field is; copyable only if all are), and the generator emits only what the traits permit: a `Vector("int_vector", "int")` contains no element-destruction code at all, and an unordered container never grows a comparison operator.
276
+
277
+ **Custom types are first-class citizens.** Your own composites join the system by implementing the same protocol in Python — then they work as element or field types everywhere, including inside intrusive containers. Your code is injected as C expressions/functions at well-defined hook points, wrapped so it cannot break operator precedence or naming conventions.
278
+
279
+ **Intrusive variants trade value space for layout.** The intrusive hash set/map store elements in one flat allocation of exactly the element type — no wrapper entries, no tag arrays — with slot states (empty/deleted) encoded as sentinel *values* you choose. If your element type can reserve two distinguishable states (e.g. `INT_MIN`/`INT_MAX` for an `int`, or low pointer values for handles), you get the most cache-friendly layout possible; if it cannot, use the regular non-intrusive containers.
280
+
281
+ **Deterministic builds.** Generation is integrated with CMake via `add_metac_module()`, SCons via `env.MetaCModule()`, and Meson via `custom_target`: output digests are tracked, code is regenerated only when definitions actually change, and every generated artifact is declared as a proper build dependency.
282
+
283
+ **Reference-counted memory as an option, not a mandate.** `Counted` references provide automatic reference counting for shared objects — including when used as container elements — while `Raw` references give you plain unmanaged pointers. Both speak the same protocol as every other type.
284
+
285
+ ## Container catalog
286
+
287
+ | Module | Type | Notes |
288
+ |---|---|---|
289
+ | `metac.vector` | `Vector` | direct-access sequence, direct-access range |
290
+ | `metac.array` | `Array` | fixed-size stack-allocated contiguous C array, direct-access range, zero heap allocation |
291
+ | `metac.packet` | `Packet` | fixed-capacity stack-allocated vector-like packet (tuple-record variant storage), direct-access range, zero heap allocations |
292
+ | `metac.circular_buffer` | `Static`, `Dynamic` | fixed-capacity stack or dynamic heap circular ring buffer, direct-access range, sortable (opt-out) |
293
+ | `metac.tiered_vector` | `Vector` | chunked append-optimized direct-access buffer — amortized O(1) push, stable addresses, direct-access range |
294
+ | `metac.list` | `List` | singly-linked sequence, forward range |
295
+ | `metac.deque` | `Deque` | doubly-linked sequence, bidirectional range |
296
+ | `metac.stack` | `Stack` | LIFO adapter over `List`, forward range |
297
+ | `metac.queue` | `Queue` | FIFO adapter over `Deque`, forward range |
298
+ | `metac.priority_queue` | `Queue` | binary heap — guaranteed O(log n) push/pop, top = greatest element, duplicate priorities allowed |
299
+ | `metac.string` | `String` | string as an index→character map, direct-access range, variadic formatted output |
300
+ | `metac.string_buffer` | `Buffer` | append-optimized string buffer with scratch accumulation and lazy joining |
301
+ | `metac.chained_hash_set` | `Set` | bucket-chaining hash set — no sentinel values, stable element references, safe default |
302
+ | `metac.intrusive_hash_set` | `Set` | flat, sentinel-based open-addressing hash set |
303
+ | `metac.treap_set` | `Set` | treap — ordered set, O(log n) expected, iterates in sorted order, bidirectional range |
304
+ | `metac.rb_set` | `Set` | red-black tree — ordered set, guaranteed O(log n) height, <= 3 rotations on delete, bidirectional range |
305
+ | `metac.avl_set` | `Set` | AVL tree — ordered set, strictly balanced height <= 1.44 log2(n), fastest lookups, bidirectional range |
306
+ | `metac.flat_set` | `Set` | contiguous sorted dynamic array with binary search lookup, cache-friendly layout |
307
+ | `metac.chained_hash_map` | `Map` | bucket-chaining hash map over internal entry set |
308
+ | `metac.intrusive_hash_map` | `Map` | flat, sentinel-based open-addressing hash map over internal entry set |
309
+ | `metac.tree_map` | `Map` | generic binary search tree map parameterized by tree set backend, bidirectional range |
310
+ | `metac.flat_map` | `Map` | ordered map over a contiguous sorted array of key-value pairs (AoS layout), bidirectional range |
311
+ | `metac.flat_multiset` | `Set` | contiguous sorted dynamic array multiset with binary search and duplicates preserved |
312
+ | `metac.counter` | `Counter` | multiset frequency counter tracking element multiplicities over an explicit map backend |
313
+ | `metac.flat_multimap` | `Map` | ordered multimap over a contiguous sorted array of key-value pairs (AoS layout) with duplicate keys preserved |
314
+ | `metac.multimap` | `Map` | generic multimap mapping keys to multiple values, parameterized by a set and collection container |
315
+ | `metac.bit_array` | `Array` | fixed-size inline bit array, set algebra, popcount, zero heap allocation |
316
+ | `metac.bit_vector` | `Vector` | dynamically resizable packed bit vector, amortized O(1) push/pop, set algebra |
317
+ | `metac.record` | `Record` | user-defined field aggregates |
318
+ | `metac.variant` | `Variant` | tagged union / sum type over alternative types |
319
+ | `metac.reference` | `Raw`, `Counted` | unmanaged / reference-counted handles |
320
+
321
+ ## Build system integration
322
+
323
+ ### CMake
324
+
325
+ ```cmake
326
+ include(MetaC) # from the generated cmake/ directory
327
+
328
+ add_metac_module(
329
+ mymodule
330
+ DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
331
+ MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/mymodule.py
332
+ COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/mymodule.py mymodule
333
+ )
334
+
335
+ target_link_libraries(myapp mymodule-auto) # header + generated sources
336
+ ```
337
+
338
+ The module bootstraps itself on first configure and re-runs the generator only when `mymodule.py` (or its stated dependencies) change. See `test/CMakeLists.txt` for a working example.
339
+
340
+ ### SCons
341
+
342
+ ```python
343
+ env = Environment(tools=["default", "metac"], toolpath=["site_scons/site_tools"])
344
+
345
+ metac_mod = env.MetaCModule("mymodule", directory=".")
346
+ env.Program(target="myapp", source=["myapp.c"] + metac_mod.sources)
347
+ ```
348
+
349
+ The SCons tool checks file timestamps against `.metac/mymodule.state` to trigger regeneration only when definitions change.
350
+
351
+ ### Meson
352
+
353
+ ```meson
354
+ # In meson.build
355
+ module_gen = custom_target('mymodule-generate',
356
+ input: 'mymodule.py',
357
+ output: 'mymodule.stamp',
358
+ command: [py, '@INPUT@', 'mymodule', '@OUTPUT@'],
359
+ build_by_default: true,
360
+ )
361
+ metac_dep = declare_dependency(
362
+ sources: [metac_sources, module_gen],
363
+ include_directories: include_directories('.')
364
+ )
365
+ executable('myapp', sources: ['myapp.c'], dependencies: [metac_dep])
366
+ ```
367
+
368
+ ## How it tests itself
369
+
370
+ The repository's test suite is written the way users write code: Python scripts in `src/metac/test/` define container instances and their unit tests, and `metac` generates a C test module (`test_auto.c`, ~2,200 lines) that is compiled and executed as part of the build. The generator is only as good as the C it produces, so that is what gets tested.
371
+
372
+ ## License
373
+
374
+ BSD 3-Clause — see [LICENSE](LICENSE).
375
+
376
+ ## The end
377
+
378
+ Happy hacking time and have fun!
379
+
380
+ Oleg A. Khlybov <fougas@mail.ru>