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
#include "Bloat.refactored.hpp"
#include "Bloat.hpp"
namespace Global
{

View File

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

View File

@ -1,9 +1,16 @@
#include "Bloat.hpp"
// #define gen_time
#include "gen.hpp"
#ifdef gen_time
namespace gen
{
ct Code make()
{
return { Code::Invalid, nullptr, nullptr, { nullptr } };
}
#if 0
static Code Unused()
{
static Code value = {
@ -13,13 +20,16 @@ namespace gen
return value;
}
#endif
Code decl_type( char const* name, Code specifiers, Code type )
{
Code
result;
result = make();
result.Type = Code::Decl_Type;
result.Name = string_make( g_allocator, name );
array_init( result.Entries, g_allocator );
result.add( specifiers );
result.add( type );
@ -33,11 +43,11 @@ namespace gen
)
{
Code
result;
result = make();
result.Type = Code::Decl_Function;
result.Name = string_make( g_allocator, name );
array_init( result.Content, g_allocator );
array_init( result.Entries, g_allocator );
if ( specifiers )
result.add( specifiers );
@ -55,20 +65,28 @@ namespace gen
if (num <= 0)
fatal("TT::make_paramters: num is %d", num);
Code result;
Code
result = make();
result.Type = Code::Parameters;
va_list va;
va_start(va, num);
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
param;
param = make();
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);
}
@ -89,12 +107,12 @@ namespace gen
va_end(va);
Code
code;
code.Name = string_make( g_allocator, fmt );
code.Type = Code::Untyped;
code.Content = string_make( g_allocator, buf );
result = make();
result.Name = string_make( g_allocator, fmt );
result.Type = Code::Untyped;
result.Content = string_make( g_allocator, buf );
return code;
return result;
}
Code make_function( char const* name
@ -104,7 +122,7 @@ namespace gen
, Code body )
{
Code
result;
result = make();
result.Name = string_make( g_allocator, name );
result.Type = Code::Function;
@ -123,37 +141,51 @@ namespace gen
return result;
}
Code make_specifier( u32 num, ... )
Code make_specifiers( u32 num, ... )
{
if ( num <= 0 )
fatal("gen::make_specifier: num cannot be zero.");
Code
result;
result = make();
result.Type = Code::Specifiers;
result.Content = string_make( g_allocator, "" );
va_list va;
va_start(va, num);
do
{
Specifier type = va_arg(va, Specifier);
Specifier type = (Specifier)va_arg(va, int);
switch ( type )
{
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;
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;
}
}
while ( num-- );
while ( --num, num );
va_end(va);
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 result = string_make( g_allocator, "" );
@ -173,12 +205,13 @@ namespace gen
case Decl_Type:
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 );
break;
case Decl_Function:
{
u32 index = 0;
u32 left = array_count( Entries );
@ -207,16 +240,22 @@ namespace gen
}
result = string_appendc( result, ");\n" );
}
break;
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 left = array_count( Entries ) - 1;
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;
case Struct:
@ -224,31 +263,36 @@ namespace gen
break;
case Function:
{
u32 index = 0;
u32 left = array_count( Entries );
if ( left <= 0 )
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++;
left--;
}
if ( left <= 0 )
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++;
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++;
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;
case Specifiers:
@ -260,18 +304,41 @@ namespace gen
break;
case Typename:
result = string_append_fmt( result, "%s", Name );
break;
}
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 );
}
}
int main()
{
gen_main();
return 0;
}
#endif gen_time
#endif

View File

@ -1,8 +1,8 @@
#pragma once
#ifdef gen_time
#include "Bloat.hpp"
#ifdef gen_time
namespace gen
{
ct sw ColumnLimit = 256;
@ -119,7 +119,7 @@ namespace gen
forceinline
operator bool()
{
return Type == Invalid;
return Type != Invalid;
}
operator char const*()
@ -180,7 +180,7 @@ namespace gen
, Code ret_type
);
Code make_parameters( u32 num, ... );
Code make_parameters( s32 num, ... );
Code make_fmt( char const* fmt, ... );
@ -197,20 +197,22 @@ namespace gen
// 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 );
struct File
struct Builder
{
zpl_file file;
string Content;
zpl_file File;
string Buffer;
s32 print( Code );
void print( Code );
bool open( char const* Path );
bool open( char const* path );
void write();
};
}
#define gen_main main
#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_build = Join-Path $path_root build
$path_scripts = Join-Path $path_root scripts
if ($false)
{
#region Regular Build
write-host "Building project`n"
if ( -not( Test-Path $path_build ) )
{
$args_meson = @()
@ -87,7 +87,13 @@ Pop-Location
$args_ninja += $path_gen_build
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

View File

@ -1,7 +1,9 @@
$path_root = git rev-parse --show-toplevel
$path_build = Join-Path $path_root build
$path_test = Join-Path $path_root test
$path_gen = Join-Path $path_test gen
$path_test_build = Join-Path $path_test build
$path_gen_build = Join-Path $path_gen build
if ( Test-Path $path_build )
{
@ -13,6 +15,11 @@ if ( Test-Path $path_test_build )
Remove-Item $path_test_build -Recurse
}
if ( Test-Path $path_gen_build )
{
Remove-Item $path_gen_build -Recurse
}
# [string[]] $include = '*.h', '*.hpp', '*.cpp'
# [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(
[
'../test'
'../project'
, '../thirdparty'
'../../project',
'../../thirdparty'
])
# get_sources = files('./get_sources.ps1')
# 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')
@ -20,6 +19,6 @@ if get_option('buildtype').startswith('debug')
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
#ifdef gen_time
#include "Bloat.hpp"
#include "gen.hpp"
#include "Bloat.hpp"
#include "gen.hpp"
#ifdef gen_time
using namespace gen;
/*
@ -15,8 +15,8 @@
return value * value;
}
*/
#define gen_square( Type_ ) gen__squre( #Type_ )
Code gen__squre( char const* type )
#define gen_square( Type_ ) gen__square( #Type_ )
Code gen__square( char const* type )
{
Code integral_type = make_type( type );
@ -41,17 +41,17 @@
u32 gen_math()
{
Code fadd_u8 = gen_square( u8 );
Code fadd_u16 = gen_square( u16 );
Code fadd_u32 = gen_square( u32 );
Code fadd_u64 = gen_square( u64 );
// Code fadd_u16 = gen_square( u16 );
// Code fadd_u32 = gen_square( u32 );
// Code fadd_u64 = gen_square( u64 );
File
Builder
mathgen;
mathgen.open( "math.gen.hpp" );
mathgen.print( fadd_u8 );
mathgen.print( fadd_u16 );
mathgen.print( fadd_u32 );
mathgen.print( fadd_u64 );
// mathgen.print( fadd_u16 );
// mathgen.print( fadd_u32 );
// mathgen.print( fadd_u64 );
mathgen.write();
return 0;
}

View File

@ -1,17 +1,28 @@
#include "Bloat.cpp"
#include "math.hpp"
#ifdef gen_time
u32 gen_main()
{
return gen_math();
}
#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
#ifndef gen_time
#include "math.hpp"
int main()
{
u32 result = square(5);