test.cpp gen_time compiles (has memory issues though)

This commit is contained in:
Edward R. Gonzalez 2023-04-02 01:07:44 -04:00
parent f09fe6aa15
commit d66c1e4eb4
13 changed files with 238 additions and 264 deletions

59
Readme.md Normal file
View File

@ -0,0 +1,59 @@
# gencpp
An attempt at simple staged metaprogramming for c/c++.
This project is not minimum feature complete yet.
Version 1 will have a most of c and a subset of c++ features available to it.
## How it works
A metaprogram is built to generate files before the main program is built. We'll term runtime for this program as `gen_time`. The metaprogram's core implementation are within `gen.hpp` and `gen.cpp` in the project directory.
`gen.cpp` \`s `main()` will expect to call one `gen_main()` which the user will have to define once for their program. There they will dictate everything that should be generated.
In order to keep the locality of this code within the same files the following pattern may be used:
Within `program.cpp` :
```cpp
#ifdef gen_time
#include "gen.hpp"
...
u32 gen_main()
{
...
}
#endif
#ifndef gen_time
#include "program.gen.cpp"
// Regular runtime dependent on the generated code here.
#endif
```
This is ofc entirely optional and the metaprogram can be quite separated from the runtime in file organization.
## Building
To fill in.
## Why
Macros in c/c++ are usually painful to debug, and templates can be unless your on a monsterous IDE (and even then fail often).
Unfortunately most programming langauges opt the approach of internally processing the generated code immediately within the AST or not expose it to the user in a nice way to even introspect as a text file. Stage metaprogramming doesn't have this problem, since its entire purpose is to create those generated files.
This is technically what the macro preprocessor does in a basic form, however naturally its easier to deal with for more complex generation.
The drawback naturally is generation functions at face value harder to grasp than something following a template pattern (for simple generation). This drawback becomes less valid the more complex the code generation becomes.
Thus a rule of thumb is if its a simple define you can get away with just the preprocessor, or if the templates being used don't break the debugger, this is most likely not neded.
However, if the code being generated becomes complex, or from a datatable or database, this will be easier to deal with.
# TODO:
* Need problably a better name, I found a few repos with this same one...
* Actually get to version 1.

View File

@ -1,5 +1,5 @@
#define BLOAT_IMPL #define BLOAT_IMPL
#include "Bloat.refactored.hpp" #include "Bloat.hpp"
namespace Global namespace Global
{ {

View File

@ -84,7 +84,7 @@ namespace Global
namespace Memory namespace Memory
{ {
ct uw Initial_Reserve = megabytes(2); ct uw Initial_Reserve = megabytes(10);
extern arena Global_Arena; extern arena Global_Arena;
#define g_allocator arena_allocator( & Memory::Global_Arena) #define g_allocator arena_allocator( & Memory::Global_Arena)

View File

@ -1,9 +1,16 @@
#include "Bloat.hpp" #include "Bloat.hpp"
// #define gen_time
#include "gen.hpp" #include "gen.hpp"
#ifdef gen_time #ifdef gen_time
namespace gen namespace gen
{ {
ct Code make()
{
return { Code::Invalid, nullptr, nullptr, { nullptr } };
}
#if 0
static Code Unused() static Code Unused()
{ {
static Code value = { static Code value = {
@ -13,13 +20,16 @@ namespace gen
return value; return value;
} }
#endif
Code decl_type( char const* name, Code specifiers, Code type ) Code decl_type( char const* name, Code specifiers, Code type )
{ {
Code Code
result; result = make();
result.Type = Code::Decl_Type; result.Type = Code::Decl_Type;
result.Name = string_make( g_allocator, name ); result.Name = string_make( g_allocator, name );
array_init( result.Entries, g_allocator );
result.add( specifiers ); result.add( specifiers );
result.add( type ); result.add( type );
@ -33,11 +43,11 @@ namespace gen
) )
{ {
Code Code
result; result = make();
result.Type = Code::Decl_Function; result.Type = Code::Decl_Function;
result.Name = string_make( g_allocator, name ); result.Name = string_make( g_allocator, name );
array_init( result.Content, g_allocator ); array_init( result.Entries, g_allocator );
if ( specifiers ) if ( specifiers )
result.add( specifiers ); result.add( specifiers );
@ -55,20 +65,28 @@ namespace gen
if (num <= 0) if (num <= 0)
fatal("TT::make_paramters: num is %d", num); fatal("TT::make_paramters: num is %d", num);
Code result; Code
result = make();
result.Type = Code::Parameters;
va_list va; va_list va;
va_start(va, num); va_start(va, num);
result.Name = string_make( g_allocator, va_arg(va, char const*) ); result.Name = string_make( g_allocator, va_arg(va, char const*) );
result.add( va_arg(va, Code) );
while( num -= 2, num % 2 ) array_init( result.Entries, g_allocator );
Code type = va_arg(va, Code);
result.add( type );
while( num -= 2, num && num % 2 == 0 )
{ {
Code Code
param; param = make();
param.Name = string_make( g_allocator, va_arg(va, char const*) ); param.Name = string_make( g_allocator, va_arg(va, char const*) );
param.add( va_arg(va, Code) );
type = va_arg(va, Code);
param.add( type );
result.add(param); result.add(param);
} }
@ -89,12 +107,12 @@ namespace gen
va_end(va); va_end(va);
Code Code
code; result = make();
code.Name = string_make( g_allocator, fmt ); result.Name = string_make( g_allocator, fmt );
code.Type = Code::Untyped; result.Type = Code::Untyped;
code.Content = string_make( g_allocator, buf ); result.Content = string_make( g_allocator, buf );
return code; return result;
} }
Code make_function( char const* name Code make_function( char const* name
@ -104,7 +122,7 @@ namespace gen
, Code body ) , Code body )
{ {
Code Code
result; result = make();
result.Name = string_make( g_allocator, name ); result.Name = string_make( g_allocator, name );
result.Type = Code::Function; result.Type = Code::Function;
@ -123,37 +141,51 @@ namespace gen
return result; return result;
} }
Code make_specifier( u32 num, ... ) Code make_specifiers( u32 num, ... )
{ {
if ( num <= 0 ) if ( num <= 0 )
fatal("gen::make_specifier: num cannot be zero."); fatal("gen::make_specifier: num cannot be zero.");
Code Code
result; result = make();
result.Type = Code::Specifiers;
result.Content = string_make( g_allocator, "" );
va_list va; va_list va;
va_start(va, num); va_start(va, num);
do do
{ {
Specifier type = va_arg(va, Specifier); Specifier type = (Specifier)va_arg(va, int);
switch ( type ) switch ( type )
{ {
case Alignas: case Alignas:
result.Content = string_sprintf_buf( g_allocator, "%s(%d)", specifier_str(type), va_arg(va, u32) ); result.Content = string_append_fmt( result.Content, "%s(%d)", specifier_str(type), va_arg(va, u32) );
break; break;
default: default:
result.Content = string_make( g_allocator, specifier_str(type) ); const char* str = specifier_str(type);
result.Content = string_append_fmt( result.Content, "%s", str );
break; break;
} }
} }
while ( num-- ); while ( --num, num );
va_end(va); va_end(va);
return result; return result;
} }
Code make_type( char const* name )
{
Code
result = make();
result.Name = string_make( g_allocator, name );
result.Type = Code::Typename;
return result;
}
string Code::to_string() string Code::to_string()
{ {
string result = string_make( g_allocator, "" ); string result = string_make( g_allocator, "" );
@ -173,12 +205,13 @@ namespace gen
case Decl_Type: case Decl_Type:
if ( Entries[0].Type == Specifiers ) if ( Entries[0].Type == Specifiers )
result = string_append_fmt("%s\n", Entries[0].to_string()); result = string_append_fmt( result, "%s\n", Entries[0].to_string());
result = string_append_fmt( result, "%s %s;\n", Entries[1].to_string(), Name ); result = string_append_fmt( result, "%s %s;\n", Entries[1].to_string(), Name );
break; break;
case Decl_Function: case Decl_Function:
{
u32 index = 0; u32 index = 0;
u32 left = array_count( Entries ); u32 left = array_count( Entries );
@ -207,16 +240,22 @@ namespace gen
} }
result = string_appendc( result, ");\n" ); result = string_appendc( result, ");\n" );
}
break; break;
case Parameters: case Parameters:
result = string_append_fmt( result, "%s %s", Entries[0], Name ); {
result = string_append_fmt( result, "%s %s", Entries[0].to_string(), Name );
u32 index = 1; u32 index = 1;
u32 left = array_count( Entries ) - 1; u32 left = array_count( Entries ) - 1;
while ( left-- ) while ( left-- )
result = string_append_fmt( result, ", %s %s", Entries[index].Entries[0], Entries[index].Name ); result = string_append_fmt( result, ", %s %s"
, Entries[index].Entries[0].to_string()
, Entries[index].Name
);
}
break; break;
case Struct: case Struct:
@ -224,31 +263,36 @@ namespace gen
break; break;
case Function: case Function:
{
u32 index = 0; u32 index = 0;
u32 left = array_count( Entries ); u32 left = array_count( Entries );
if ( left <= 0 ) if ( left <= 0 )
fatal( "Code::to_string - Name: %s Type: %s, expected definition", Name, Type ); fatal( "Code::to_string - Name: %s Type: %s, expected definition", Name, Type );
while ( Entries[index].Type == EType::Specifiers ) if ( Entries[index].Type == Specifiers )
{ {
result = string_append_fmt( result, "%s ", Entries[index] ); result = string_append_fmt( result, "%s\n", Entries[index].to_string() );
index++; index++;
left--;
} }
if ( left <= 0 ) if ( left <= 0 )
fatal( "Code::to_string - Name: %s Type: %s, expected return type", Name, Type ); fatal( "Code::to_string - Name: %s Type: %s, expected return type", Name, Type );
result = string_append_fmt( result, "\n%s %s(", Entries[index], Name ); result = string_append_fmt( result, "\n%s %s(", Entries[index].to_string(), Name );
index++; index++;
left--;
while ( left && Entries[index].Type == Parameters ) if ( left && Entries[index].Type == Parameters )
{ {
result = string_append_fmt( result, "%s, ", Entries[index] ); result = string_append_fmt( result, "%s, ", Entries[index].to_string() );
index++; index++;
left--;
} }
result = string_append_fmt( result, ")\n{\n%s\n}", Entries[index] ); result = string_append_fmt( result, ")\n{\n%s\n}", Entries[index].to_string() );
}
break; break;
case Specifiers: case Specifiers:
@ -260,18 +304,41 @@ namespace gen
break; break;
case Typename: case Typename:
result = string_append_fmt( result, "%s", Name );
break; break;
} }
return result; return result;
} }
void Builder::print( Code code )
{
Buffer = string_append( Buffer, code.to_string() );
}
bool Builder::open( char const* path )
{
file_error error = file_open_mode( & File, ZPL_FILE_MODE_WRITE, path );
if ( error != ZPL_FILE_ERROR_NONE )
{
fatal( "gen::File::open - Could not open file: %s", path);
return false;
}
Buffer = string_make( g_allocator, "" );
return true;
}
void Builder::write()
{
bool result = file_write( & File, Buffer, string_length(Buffer) );
if ( result == false )
fatal("gen::File::write - Failed to write to file: %s", file_name( & File ) );
file_close( & File );
}
} }
#endif
int main()
{
gen_main();
return 0;
}
#endif gen_time

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#ifdef gen_time
#include "Bloat.hpp" #include "Bloat.hpp"
#ifdef gen_time
namespace gen namespace gen
{ {
ct sw ColumnLimit = 256; ct sw ColumnLimit = 256;
@ -119,7 +119,7 @@ namespace gen
forceinline forceinline
operator bool() operator bool()
{ {
return Type == Invalid; return Type != Invalid;
} }
operator char const*() operator char const*()
@ -153,13 +153,13 @@ namespace gen
#endif #endif
#pragma endregion Member API #pragma endregion Member API
#define Using_Code_POD \ #define Using_Code_POD \
Code::EType Type; \ Code::EType Type; \
string Name; \ string Name; \
string Comment; \ string Comment; \
union { \ union { \
array(Code) Entries; \ array(Code) Entries; \
string Content; \ string Content; \
}; };
Using_Code_POD; Using_Code_POD;
@ -180,7 +180,7 @@ namespace gen
, Code ret_type , Code ret_type
); );
Code make_parameters( u32 num, ... ); Code make_parameters( s32 num, ... );
Code make_fmt( char const* fmt, ... ); Code make_fmt( char const* fmt, ... );
@ -197,20 +197,22 @@ namespace gen
// Code make_template( Code subject, u32 num_dependents, ... ); // Code make_template( Code subject, u32 num_dependents, ... );
// Code make_type( char const* name ); Code make_type( char const* name );
// Code make_using( char const* name, char const* type ); // Code make_using( char const* name, char const* type );
struct File struct Builder
{ {
zpl_file file; zpl_file File;
string Content; string Buffer;
s32 print( Code ); void print( Code );
bool open( char const* Path ); bool open( char const* path );
void write(); void write();
}; };
} }
#define gen_main main
#endif #endif

View File

@ -14,16 +14,16 @@ foreach ( $arg in $args )
} }
if ($false)
{
#region Regular Build
write-host "Building project`n"
$path_root = git rev-parse --show-toplevel $path_root = git rev-parse --show-toplevel
$path_build = Join-Path $path_root build $path_build = Join-Path $path_root build
$path_scripts = Join-Path $path_root scripts $path_scripts = Join-Path $path_root scripts
if ($false)
{
#region Regular Build
write-host "Building project`n"
if ( -not( Test-Path $path_build ) ) if ( -not( Test-Path $path_build ) )
{ {
$args_meson = @() $args_meson = @()
@ -87,7 +87,13 @@ Pop-Location
$args_ninja += $path_gen_build $args_ninja += $path_gen_build
Push-Location $path_root Push-Location $path_root
ninja $args_ninja & ninja $args_ninja
Pop-Location
$gencpp = Join-Path $path_gen_build gencpp.exe
Push-location $path_gen
& $gencpp
Pop-Location Pop-Location

View File

@ -1,7 +1,9 @@
$path_root = git rev-parse --show-toplevel $path_root = git rev-parse --show-toplevel
$path_build = Join-Path $path_root build $path_build = Join-Path $path_root build
$path_test = Join-Path $path_root test $path_test = Join-Path $path_root test
$path_gen = Join-Path $path_test gen
$path_test_build = Join-Path $path_test build $path_test_build = Join-Path $path_test build
$path_gen_build = Join-Path $path_gen build
if ( Test-Path $path_build ) if ( Test-Path $path_build )
{ {
@ -13,6 +15,11 @@ if ( Test-Path $path_test_build )
Remove-Item $path_test_build -Recurse Remove-Item $path_test_build -Recurse
} }
if ( Test-Path $path_gen_build )
{
Remove-Item $path_gen_build -Recurse
}
# [string[]] $include = '*.h', '*.hpp', '*.cpp' # [string[]] $include = '*.h', '*.hpp', '*.cpp'
# [string[]] $exclude = # [string[]] $exclude =

View File

@ -1,13 +0,0 @@
// Handwritten generated code:
#pragma region gen_array
#pragma endregion gen_array
#define Array( Type_ ) Array_##Type_

View File

@ -1,164 +0,0 @@
#include "gen.hpp"
#include "bloat.hpp"
#if tt
#define gen_array( Type_ ) gen__array( #Type_ )
Code* gen__array( char const* type )
{
Code codegen;
Code
data;
data.add( "%type* data" );
Code header;
header.define_struct( "Header",
"uw Num;"
"uw Capacity;"
"allocator Allocator;"
);
Code grow_formula;
grow_formula.define_function( "grow_formula", "static", "forceinline",
"return 2 * value * 8"
);
codegen.define_struct( "Array",
data
, header
, grow_formula
);
codegen.define
R"(
%type* data;
struct Header
{
uw Num;
uw Capacity;
allocator Allocator;
};
static forceinline
sw grow_formula ( sw value )
{
return 2 * value * 8;
}
static
bool init ( %Array& array, allocator mem_handler )
{
return init_reserve( array, mem_handler, grow_formula(0) );
}
static
bool init_reserve( %Array& array, allocator mem_handler, uw capacity )
{
Header*
header = nullptr;
header = rcast( Header*, alloc( mem_handler, size_of( Header ) + sizeof(type) * capacity ));
if (header == nullptr)
return false;
header->Num = 0;
header->Capacity = capacity;
header->Allocator = mem_handler;
array.data = rcast( %type*, header + 1 );
return true;
}
void append( %type value )
{
}
type back()
{
Header& header = get_header();
return data[ header.Num - 1 ];
}
void clear()
{
get_header().Num = 0;
}
void fill( uw begin, uw end, type value )
{
Header& header = get_header();
if ( begin < 0 || end >= header.Num )
{
fatal( "Range out of bounds" );
}
}
void free()
{
Header& header = get_header();
::free( header.Allocator, & get_header() );
}
bool grow( uw min_capacity )
{
Header& header = get_header();
uw new_capacity = grow_formula( header.Capacity );
if ( new_capacity < min_capacity )
new_capacity = min_capacity;
return set_capacity( new_capacity );
}
forceinline
Header& get_header()
{
return vcast( Header, data - 1 );
}
void pop()
{
}
void reserve()
{
}
void resize()
{
}
bool set_capacity( uw capacity )
{
Header& header = get_header();
if ( capacity == header.Capacity )
{
}
}
)"
, type
);
};
#endif
void tt_run()
{
gen_array( u32 );
}
#endif tt
#if !tt
#include "array.gen.manual.hpp"
#endif

0
test/gen/math.gen.hpp Normal file
View File

View File

@ -4,15 +4,14 @@ project( 'test', 'c', 'cpp', default_options : ['buildtype=debug'] )
includes = include_directories( includes = include_directories(
[ [
'../test' '../../project',
'../project' '../../thirdparty'
, '../thirdparty'
]) ])
# get_sources = files('./get_sources.ps1') # get_sources = files('./get_sources.ps1')
# sources = files(run_command('powershell', get_sources, check: true).stdout().strip().split('\n')) # sources = files(run_command('powershell', get_sources, check: true).stdout().strip().split('\n'))
sources = [ 'test.cpp' ] sources = [ '../test.cpp' ]
if get_option('buildtype').startswith('debug') if get_option('buildtype').startswith('debug')
@ -20,6 +19,6 @@ if get_option('buildtype').startswith('debug')
endif endif
add_project_arguments('-Dgen_time', langauge : ['c', 'cpp']) add_project_arguments('-Dgen_time', language : ['c', 'cpp'])
executable( '', sources, include_directories : includes ) executable( 'gencpp', sources, include_directories : includes )

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#ifdef gen_time #include "Bloat.hpp"
#include "Bloat.hpp" #include "gen.hpp"
#include "gen.hpp"
#ifdef gen_time
using namespace gen; using namespace gen;
/* /*
@ -15,8 +15,8 @@
return value * value; return value * value;
} }
*/ */
#define gen_square( Type_ ) gen__squre( #Type_ ) #define gen_square( Type_ ) gen__square( #Type_ )
Code gen__squre( char const* type ) Code gen__square( char const* type )
{ {
Code integral_type = make_type( type ); Code integral_type = make_type( type );
@ -41,17 +41,17 @@
u32 gen_math() u32 gen_math()
{ {
Code fadd_u8 = gen_square( u8 ); Code fadd_u8 = gen_square( u8 );
Code fadd_u16 = gen_square( u16 ); // Code fadd_u16 = gen_square( u16 );
Code fadd_u32 = gen_square( u32 ); // Code fadd_u32 = gen_square( u32 );
Code fadd_u64 = gen_square( u64 ); // Code fadd_u64 = gen_square( u64 );
File Builder
mathgen; mathgen;
mathgen.open( "math.gen.hpp" ); mathgen.open( "math.gen.hpp" );
mathgen.print( fadd_u8 ); mathgen.print( fadd_u8 );
mathgen.print( fadd_u16 ); // mathgen.print( fadd_u16 );
mathgen.print( fadd_u32 ); // mathgen.print( fadd_u32 );
mathgen.print( fadd_u64 ); // mathgen.print( fadd_u64 );
mathgen.write(); mathgen.write();
return 0; return 0;
} }

View File

@ -1,17 +1,28 @@
#include "Bloat.cpp"
#include "math.hpp" #include "math.hpp"
#ifdef gen_time #ifdef gen_time
u32 gen_main()
{
return gen_math();
}
#include "gen.cpp" #include "gen.cpp"
int gen_main()
{
Memory::setup();
zpl_printf("\nPress any key after attaching to process\n");
getchar();
int result = gen_math();
Memory::cleanup();
return result;
}
#endif #endif
#ifndef gen_time #ifndef gen_time
#include "math.hpp"
int main() int main()
{ {
u32 result = square(5); u32 result = square(5);