mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-31 06:50:53 -07:00 
			
		
		
		
	Updates to gen_c_library docs
This commit is contained in:
		| @@ -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 <compiler> <debug or omit> c_library | ||||
| .\build.ps1 <compiler> <debug or omit> c_lib | ||||
| ``` | ||||
|  | ||||
| To generate a static or dynamic library: | ||||
|  | ||||
| ```ps1 | ||||
| .\build.ps1 <compiler> <debug or omit> 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<Type>` `HashTable<Type>` within [containers.hpp](../base/dependencies/containers.hpp) | ||||
|  | ||||
| Both files follow the same patter of providing three procedures: | ||||
|  | ||||
| * `gen_<container>_base` : Intended to be called once, defines universal "base" definitions. | ||||
| * `gen_<container>` : Called per instatiation of the container for a given set of dependent args. | ||||
| * `gen_<container>_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 `<container>_DefinitionCounter` is used to know how many instantiations of the template have occured. This is used to determine how to define `GENERIC_SLOT_<ID>_<functionID>` 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. | ||||
|   | ||||
| @@ -1139,15 +1139,6 @@ R"(#define <interface_name>( 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 <interface_name>( 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 <interface_name>( 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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user