mirror of
https://github.com/Ed94/gencpp.git
synced 2024-12-22 07:44:45 -08:00
Compare commits
2 Commits
e4f564b165
...
772d0de5c1
Author | SHA1 | Date | |
---|---|---|---|
772d0de5c1 | |||
cb50f93af5 |
@ -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.
|
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 <compiler> <debug or omit> base
|
||||||
|
```
|
||||||
|
|
||||||
Standard formats:
|
Standard formats:
|
||||||
|
|
||||||
* **base**: Files are in granular pieces separated into four directories:
|
* **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_segemetned**: Dependencies go into gen.dep.{hpp/cpp} and components into gen.{hpp/cpp}
|
||||||
* **gen_singleheader**: Everything into a single file: gen.hpp
|
* **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_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/<auxiliary_name>.<hpp/cpp>`. These are optional extensions or tools for the library.
|
Code not making up the core library is located in `auxiliary/<auxiliary_name>.<hpp/cpp>`. These are optional extensions or tools for the library.
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
## Navigation
|
## Navigation
|
||||||
|
|
||||||
# base
|
|
||||||
|
|
||||||
[Top](../Readme.md)
|
[Top](../Readme.md)
|
||||||
|
|
||||||
* [docs](../docs/Readme.md)
|
* [docs](../docs/Readme.md)
|
||||||
@ -15,9 +13,133 @@ The output will be in the `gen_segmented/gen` directory (if the directory does n
|
|||||||
If using the library's provided build scripts:
|
If using the library's provided build scripts:
|
||||||
|
|
||||||
```ps1
|
```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).
|
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.**
|
**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 scanned in 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 minimal 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 are 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 (psuedo-C):
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define type_expr_pair(type, expr) type: expr
|
||||||
|
|
||||||
|
C_Expression _Generic( selector_arg, a_type_expr_pair, ... ) {
|
||||||
|
switch( typeof(selector_arg)) {
|
||||||
|
case a_type_expr_pair:
|
||||||
|
return a_type_expr_pari.expr;
|
||||||
|
...
|
||||||
|
default:
|
||||||
|
return default.expr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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 '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);
|
CodeFn fn = cast(CodeFn, entry);
|
||||||
Code prev = entry->Prev;
|
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;
|
b32 handled= false;
|
||||||
for ( CodeParams opt_param : fn->Params ) if (opt_param->ValueType->Name.starts_with(txt("Opts_")))
|
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);
|
CodeFn fn = cast(CodeFn, entry);
|
||||||
Code prev = entry->Prev;
|
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);
|
src_interface.append(fn);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1450,18 +1430,6 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
|||||||
{
|
{
|
||||||
CodeFn fn = cast(CodeFn, entry);
|
CodeFn fn = cast(CodeFn, entry);
|
||||||
Code prev = entry->Prev;
|
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_")))
|
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
|
// The frontend names are warapped in macros so we need to give it the intenral symbol name
|
||||||
|
@ -15,11 +15,13 @@ Its main uage is the [c_library generation](../gen_c_library/).
|
|||||||
Remove any generated content from the repository.
|
Remove any generated content from the repository.
|
||||||
|
|
||||||
**`build.ps1`**
|
**`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:
|
args:
|
||||||
c_library
|
c_lib : Build c11 library (singleheader & segmented)
|
||||||
|
c_lib_static : Build static c11 library
|
||||||
|
c_lib_dyn : Buidl dyanmic c11
|
||||||
segemented
|
segemented
|
||||||
singleheader
|
singleheader
|
||||||
unreal
|
unreal
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#define GEN_IMPLEMENTATION
|
#define GEN_IMPLEMENTATION
|
||||||
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
|
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
|
||||||
#define GEN_ENFORCE_STRONG_CODE_TYPES
|
|
||||||
#include "gen_singleheader.h"
|
#include "gen_singleheader.h"
|
||||||
|
|
||||||
#define gen_iterator( Type, container, iter ) \
|
#define gen_iterator( Type, container, iter ) \
|
||||||
|
Loading…
Reference in New Issue
Block a user