From cb50f93af51f5fb96f9805ba722828cfd4f6a2e0 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Mon, 16 Dec 2024 00:47:46 -0500 Subject: [PATCH] Updates to gen_c_library docs --- base/Readme.md | 8 ++- gen_c_library/Readme.md | 120 +++++++++++++++++++++++++++++++++++- gen_c_library/c_library.cpp | 32 ---------- scripts/Readme.md | 8 ++- test/c_library/test.c | 1 - 5 files changed, 129 insertions(+), 40 deletions(-) diff --git a/base/Readme.md b/base/Readme.md index b7c840e..fb50225 100644 --- a/base/Readme.md +++ b/base/Readme.md @@ -8,6 +8,12 @@ The library is fragmented into a series of headers and source files meant to be scanned in and then generated to a standard target format, or a user's desires. +If using the library's provided build scripts: + +```ps1 +.\build.ps1 base +``` + Standard formats: * **base**: Files are in granular pieces separated into four directories: @@ -27,7 +33,7 @@ Standard formats: * **gen_segemetned**: Dependencies go into gen.dep.{hpp/cpp} and components into gen.{hpp/cpp} * **gen_singleheader**: Everything into a single file: gen.hpp * **gen_unreal_engine**: Like gen_segemented but the library is modified slightly to compile as a thirdparty library within an Unreal Engine plugin or module. -* **gen_c_library**: The library is heavily modifed into C11 compliant code. A segemented and single-header set of variants are generatd. +* **gen_c_library**: The library is heavily modifed into C11 compliant code. A segemented and single-header set of variants are generated. Code not making up the core library is located in `auxiliary/.`. These are optional extensions or tools for the library. diff --git a/gen_c_library/Readme.md b/gen_c_library/Readme.md index 992f0f3..79a10f8 100644 --- a/gen_c_library/Readme.md +++ b/gen_c_library/Readme.md @@ -1,7 +1,5 @@ ## Navigation -# base - [Top](../Readme.md) * [docs](../docs/Readme.md) @@ -15,9 +13,125 @@ The output will be in the `gen_segmented/gen` directory (if the directory does n If using the library's provided build scripts: ```ps1 -.\build.ps1 c_library +.\build.ps1 c_lib +``` + +To generate a static or dynamic library: + +```ps1 +.\build.ps1 c_lib_static c_lib_dyn ``` All free from tag identifiers will be prefixed with `gen_` or `GEN_` as the namespace. This can either be changed after generation with a `.refactor` script (or your preferred subst method), OR by modifying [c_library.refactor](./c_library.refactor). **If c_library.refactor is modified you may need to modify c_library.cpp and its [components](./components/). As some of the container generation relies on that prefix.** + +## Generation structure + +1. Files are scaned or parsed + * If they are parsed, its dude to requiring some changes to either naming, or adding additonal definitions (container generation, typedefs, etc). +2. All scanned or parsed code is refactored (identifiers substs) and/or formatted. +3. Singleheader generated. +4. Segemented headers and source generated. + +## Templated container generation + +The array and hashtable containers used across this library are generated using the following implementatioon: + +* [containers.array.hpp](./components/containers.array.hpp) +* [containers.hashtable.hpp](./components/containers.hashtable.hpp) + +These are functionally (and interface wise) equivalent to the library's `Array` `HashTable` within [containers.hpp](../base/dependencies/containers.hpp) + +Both files follow the same patter of providing three procedures: + +* `gen__base` : Intended to be called once, defines universal "base" definitions. +* `gen_` : Called per instatiation of the container for a given set of dependent args. +* `gen__generic_selection_interface` : Intended to be called once after all of the instantiated containers have finished generating. It will generate a set of generic selection macros as described by Macro Usage section below. + +A simple `_DefinitionCounter` is used to know how many instantiations of the template have occured. This is used to determine how to define `GENERIC_SLOT__` for the generic interface along with how many slots the `_Generic` macro will need to have generated. + +## Macro Usage + +For the most part macros are kept low usage wise with exception to `_Generic`... +*(I will be explaining this thing for the rest of this seciton along with gencpp c library's usage of it)* + +The `_Generic` macro plays a key role in reducing direct need of the user to wrangle with mangled definition identifiers of 'templated' containers or for type coercion to map distinct data types to a common code path. + +Because of its lack of use in many C11 libraries and of those that do, they usually end up obfuscating it with excessive preprocessor abuse; Effort was put into minimizing how much of these macros was handled by the preprocessor vs gencpp itself. + +The usual presentation (done bare) is the following: + +```c +#define macro_that_selects_typeof_arg(arg, y) \ +_Generic( (arg), \ + int : some expression, \ + double : some other expression, \ + struct Whatnot : something else again, \ + default : fallback expression \ +) +``` + +Where `_Generic` can be considered the follwoing: + +```c +#define type_expr_pair(type, expr) type: expr + +expr _Generic( selector_arg, a_type_expr_pair, ... ) +``` + +The first `arg` of _Generic behaves as the "controlling expression" or the expression that resolves to a type which will dictate which of the following expressions provided after to `_Generic` will be resolved as the one used inline for the implemenation. + + +For this library's purposes we'll be using the functional macro equivalent *(if there is an excpetion I'll link it at the end fo the section)*: + +```c +#define macro_that_uses_selector_arg_for_resolving_a_fucntion( selecting_exp) \ +_Generic( (arg), \ + int : func_use_int, \ + double : func_use_double, \ + struct Whatnot : func_use_Whatnot, \ + default : struct SIGNALS_FAILURE \ +) (selecting_exp) +``` + +In this case, we directly added `(selecting_exp)` to the end there.. as we want to directly have the macro resolve to calling a resolved procedure. A default has been set to a struct as that leads to a neat compiler message that would otherwise be impossible beause static_assert is a statement and thus cannot be used within a slot. + +Now, even with gencpp generating this type-expression table, we still need wrapper macros to achieve function 'overloading' for the templated containers as _Generic has a [significant drawback](https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/c11-generic/): + +> Discarded expressions still have to be semantically valid. + +The only way to absolve this issue [(without resorting to nasty preprocessor hacks)](https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md) is with wrapping expressions in a 'slot' resolving macros that do not expand if the slot is not defined: + +```c +GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GENERIC_SLOT_1__function_sig ) +``` + +`GENERIC_SLOT_1__function_sig` is our warpper of a "`int, func_use_int`" pair. The `GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT` is a verbse named macro to indicate that that pair will be expanded ONLY IF its defined. + +So for any given templated container interface. Expect the follwoing (taken straight from generation, and just cleaned up formatting...): + +```c +#define gen_array_append( selector_arg, ... ) _Generic( \ +(selector_arg ), \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_1__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_2__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_3__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_4__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_5__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_6__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_7__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_8__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_9__array_append ) \ + GEN_IF_MACRO_DEFINED_INCLUDE_THIS_SLOT( GEN_GENERIC_SLOT_10__array_append ) \ + default: gen_generic_selection_fail \ +) GEN_RESOLVED_FUNCTION_CALL( &selector_arg, __VA_ARGS__ ) + +``` + +*Note(Ed): Unfortunately I cannot get clang-format to output these macros sanely like the above..* +*Eventually I'll add some basic builtin formatting but if the user has suggestions for something better I'm open ears...* + +`GEN_RESOLVED_FUNCTION_CALL` is an empty define, its just to indicate that its intended to expand to a function call. + +To see the thea actual macro definitions used: [generic_macros.h](./components/generic_macros.h) has them. They'll be injected right after the usual macros are positioned in the header file. diff --git a/gen_c_library/c_library.cpp b/gen_c_library/c_library.cpp index 1db0f2a..812a081 100644 --- a/gen_c_library/c_library.cpp +++ b/gen_c_library/c_library.cpp @@ -1139,15 +1139,6 @@ R"(#define ( code ) _Generic( (code), \ CodeFn fn = cast(CodeFn, entry); Code prev = entry->Prev; - #if 0 - if (prev && prev->Name.is_equal(entry->Name)) { - // rename second definition so there isn't a symbol conflict - StrBuilder postfix_arr = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "%S_arr", entry->Name); - entry->Name = cache_str(postfix_arr.to_str()); - postfix_arr.free(); - } - #endif - b32 handled= false; for ( CodeParams opt_param : fn->Params ) if (opt_param->ValueType->Name.starts_with(txt("Opts_"))) { @@ -1373,17 +1364,6 @@ R"(#define ( code ) _Generic( (code), \ { CodeFn fn = cast(CodeFn, entry); Code prev = entry->Prev; - #if 0 - for ( CodeParams arr_param : fn->Params ) - { - b32 repeat_register_macros = fn->Name.is_equal(txt("register_macros")) && arr_param->Name.is_equal(txt("num")) && ! arr_param->Next->Name.is_equal(txt("...")); - if ( repeat_register_macros ) { - // rename second definition so there isn't a symbol conflict - StrBuilder postfix_arr = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "%S_arr", fn->Name); - fn->Name = cache_str(postfix_arr.to_str()); - } - } - #endif src_interface.append(fn); } break; @@ -1450,18 +1430,6 @@ R"(#define ( code ) _Generic( (code), \ { CodeFn fn = cast(CodeFn, entry); Code prev = entry->Prev; - - #if 0 - for ( CodeParams arr_param : fn->Params ) - { - b32 repeat_def_array = fn->Name.starts_with(txt("def_")) && arr_param->Name.is_equal(txt("num")) && ! arr_param->Next->Name.is_equal(txt("...")); - if ( repeat_def_array ) { - // rename second definition so there isn't a symbol conflict - StrBuilder postfix_arr = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "%S_arr", fn->Name); - fn->Name = cache_str(postfix_arr.to_str()); - } - } - #endif for ( CodeParams opt_param : fn->Params ) if (opt_param->ValueType->Name.starts_with(txt("Opts_"))) { // The frontend names are warapped in macros so we need to give it the intenral symbol name diff --git a/scripts/Readme.md b/scripts/Readme.md index 852e982..8a4e29f 100644 --- a/scripts/Readme.md +++ b/scripts/Readme.md @@ -15,11 +15,13 @@ Its main uage is the [c_library generation](../gen_c_library/). Remove any generated content from the repository. **`build.ps1`** -Build c_library, segmented, singleheader, unreal. Supports msvc or clang, release or debug. +Build c library (segmented, singleheader, static, or dynamic), cpp library (segmented, singleheader, or unreal). Supports msvc or clang, release or debug. -``` +```erlang args: - c_library + c_lib : Build c11 library (singleheader & segmented) + c_lib_static : Build static c11 library + c_lib_dyn : Buidl dyanmic c11 segemented singleheader unreal diff --git a/test/c_library/test.c b/test/c_library/test.c index 78e7ccd..85288ef 100644 --- a/test/c_library/test.c +++ b/test/c_library/test.c @@ -1,6 +1,5 @@ #define GEN_IMPLEMENTATION #define GEN_DEFINE_LIBRARY_CODE_CONSTANTS -#define GEN_ENFORCE_STRONG_CODE_TYPES #include "gen_singleheader.h" #define gen_iterator( Type, container, iter ) \