Moved tmm_alloc_args to it's own file. Updates to convert_swig to

generate alloc functions in the swig interface files for classes.
These alloc functions wrap the tmm_alloc_args call so swig doesn't have
to think about variadic templates.
This commit is contained in:
Mark Herring 2024-09-25 02:45:41 -05:00
parent 1e5d1a8cc4
commit 59537e487c
9 changed files with 249 additions and 41 deletions

View File

@ -0,0 +1,40 @@
/*************************************************************************
PURPOSE: (Trick TMM Alloc W/ Args)
ICG: (No)
LIBRARY DEPENDENCY:
(
()
)
**************************************************************************/
#ifndef __ALLOC_WITH_ARGS_HH__
#define __ALLOC_WITH_ARGS_HH__
#include "trick/trick_type_traits.hh"
/*
In the case that a TrickTypeToString<T> for some type T exist, then this function is valid and will be compiled.
*/
template<typename T, typename ...Args>
typename std::enable_if<Trick::has_getname<T>::value, T*>::type
tmm_alloc_args(Args&&... args)
{
void* new_alloc = trick_MM->declare_var(TrickTypeToString<T>::getName().c_str());
std::cout << "Allocating: " << TrickTypeToString<T>::getName() << std::endl;
return new (new_alloc) T(std::forward<Args>(args)...);
}
/*
In the case that a TrickTypeToString<T> for some type T does NOT exist, then this function is valid and will be compiled.
*/
template<typename T, typename ...Args>
typename std::enable_if<!Trick::has_getname<T>::value, T*>::type
tmm_alloc_args(Args&&... args)
{
static_assert(Trick::always_false<T>::value,
"You've attempted to call tmm_alloc_args using a type(T) that does not have an implemented specialization for TrickTypeToString.");
return nullptr;
}
#endif

View File

@ -39,32 +39,9 @@ struct has_getname : std::false_type {};
template<typename T>
struct has_getname<T, void_t<decltype(std::declval<TrickTypeToString<T>>().getName())>> : std::true_type {};
/*
In the case that a TrickTypeToString<T> for some type T exist, then this function is valid and will be compiled.
*/
template<typename T, typename ...Args>
typename std::enable_if<has_getname<T>::value, T*>::type
tmm_alloc_args(Args&&... args)
{
void* new_alloc = trick_MM->declare_var(TrickTypeToString<T>::getName().c_str());
return new (new_alloc) T(std::forward<Args>(args)...);
}
/*
In the case that a TrickTypeToString<T> for some type T does NOT exist, then this function is valid and will be compiled.
*/
template<typename T, typename ...Args>
typename std::enable_if<!has_getname<T>::value, T*>::type
tmm_alloc_args(Args&&... args)
{
static_assert(always_false<T>::value,
"You've attempted to call tmm_alloc_args using a type(T) that does not have an implemented specialization for TrickTypeToString.");
return nullptr;
}
}
#endif //__TMM_ALLOC_ARGS_HH__

View File

@ -318,6 +318,7 @@ sub process_file() {
\%insert(\"begin\") \%{
#include <Python.h>
#include <cstddef>
#include \"trick/tmm_alloc_args.hh\"
\%}
\%{
@ -610,6 +611,8 @@ sub process_class($$$$$) {
my $extracted ;
my ($class_name) ;
my $template_typedefs ;
my $extend_block;
my $ignore_constructors;
## Extract the class_name from the class_string
$class_string =~ /^(?:class|struct)\s+ # keyword class or struct
@ -628,16 +631,87 @@ sub process_class($$$$$) {
$my_class_contents .= "\n#if SWIG_VERSION > 0x040000\n\%pythoncode \%{\n __setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n\%}\n#endif\n" ;
($extracted, $$contents_ref) = extract_bracketed( "{" . $$contents_ref , "{}") ;
# remove the trailing semicolon because we may append text to the class.
$$contents_ref =~ s/^\s*;//s ;
#remove added extra opening "brace"
$extracted =~ s/^\{// ;
#print "*** extracted = $extracted ***\n" ;
#print "*** contents = $$contents_ref ***\n" ;
#
if(!is_abstract_class($extracted)) {
my @constructors;
#Find constructors
while($extracted =~ /\b$class_name\s*\(([^)]*)\)\s*(?:const)?\s*;?/gs) {
my $params = $1;
if($params !~ /^\s*$/) {
push @constructors, $params;
}
}
# Only proceed if there are non-default constructors
if (@constructors) {
my $qualified_class = $curr_namespace ? "$curr_namespace::$class_name" : $class_name;
$extend_block = "\%extend $qualified_class {\n";
foreach my $constructor (@constructors) {
#my $wrapper_name .= $class_name;
# Parse parameters into types and names
my $params_ref = parse_parameters($constructor);
my @param_types = @{ $params_ref->{types} };
my @param_names = @{ $params_ref->{names} };
if(is_copy_constructor($class_name, \@param_types)) {
print "COPY CONSTRUCTOR in $class_name\n";
next;
}
# Validate parameter counts
die "Mismatch between parameter types and names for class $class_name"
unless scalar(@param_types) == scalar(@param_names);
# Combine types and names
#$wrapper_name .= join('_', $param_types[$_]);
my @type_name_pairs = map { "$param_types[$_] $param_names[$_]" } (0 .. $#param_types);
my $param_list = join(', ', @type_name_pairs);
my @clean_param_names = map {
my $type = $_;
$type =~ s/\s*&\s*//g;
$type
} @param_names;
# Debugging
#print STDERR "Debug: Generating constructor for '$class_name' with parameters: $param_list\n";
# Construct constructor signature
my $signature = "static $class_name* alloc($param_list)";
#Construct tmm_alloc_args call with parameter names
my $tmm_call = " return tmm_alloc_args<$qualified_class>(";
$tmm_call .= join(', ', @clean_param_names);
$tmm_call .= ");\n";
# Append constructor to %extend block
$extend_block .= " $signature {\n$tmm_call";
$extend_block .= " }\n";
#print STDERR "Wrapper name: $wrapper_name\n";
}
$extend_block .= "};\n";
# Debugging: Print the generated %extend block
#print STDERR "Debug: Generated %extend block for '$class_name':\n$extend_block\n\n";
}
}
# SWIG doesn't like "const static". Change it to "static const"
$extracted =~ s/const\s+static/static const/g ;
@ -744,6 +818,9 @@ sub process_class($$$$$) {
$$new_contents_ref .= $my_class_contents ;
# write the class contents and semicolon to ensure any template declarations below are after the semicolon.
$$new_contents_ref .= $extracted . ";\n" ;
#$$new_contents_ref .= $ignore_constructors;
$$new_contents_ref .= $extend_block . "\n\n";
my $c_ = "$curr_namespace$class_name" ;
$c_ =~ s/\:/_/g ;
@ -752,6 +829,7 @@ sub process_class($$$$$) {
$$new_contents_ref .= "#define TRICK_SWIG_DEFINED_$c_" ;
}
## ================================================================================
## process_typedef_struct
##
@ -839,4 +917,91 @@ sub process_typedef_const_struct($$$$) {
}
# Helper function to parse parameters
sub parse_parameters {
my ($param_string) = @_;
my @types = ();
my @names = ();
my $arg_counter = 1;
# Return empty arrays if no parameters
return { types => \@types, names => \@names } if $param_string =~ /^\s*$/;
my @params = split /,/, $param_string;
foreach my $param (@params) {
# Trim leading and trailing whitespace
$param =~ s/\s*=\s*[^,]+//;
# Remove default values (e.g., '= 0')
$param =~ s/^\s+|\s+$//g;
if ($param =~ /^(.*\S)\s+(\S+)\s*$/) {
my $type = $1 || '';
my $name = $2;
if($name =~ s/\*//g) {
#This variable is a pointer, move the pointer to the type
$type .= "*";
} elsif ($name =~ s/\&//g) {
#This variable is a reference, move the pointer to the type
$type .= "&";
}
# Clean up the type
$type =~ s/\s*&\s*/&/g; # Remove spaces around '&'
$type =~ s/\s*\*\s*/\*/g; # Remove spaces around '*'
$type =~ s/\s+/ /g; # Replace multiple spaces with a single space
$type =~ s/^\s+|\s+$//g; # Trim leading/trailing whitespace
# Clean up the name
$name =~ s/^\s+|\s+$//g;
push @types, $type;
push @names, $name;
} else {
# Handle cases where the name might be missing
my $type = $param;
$type =~ s/^\s+|\s+$//g;
push @types, $type;
push @names, "arg" . scalar(@names);
}
}
return { types => \@types, names => \@names };
}
sub is_copy_constructor {
my ($class_name, $param_types_ref) = @_;
my @param_types = @$param_types_ref;
#Anything that has more than 1 argument is definitely
#not a copy constructor
return 0 unless scalar(@param_types) == 1;
my $param_type = $param_types[0];
#Remove attributes
my $clean_param_type = $param_type;
$clean_param_type =~ s/\bconst\b\s*//g; # Remove 'const'
$clean_param_type =~ s/\s*&\s*//g; # Remove '&'
$clean_param_type =~ s/\s*&&\s*//g; # Remove '&&' (for move constructors)
$clean_param_type =~ s/^\s+|\s+$//g; # Trim leading/trailing whitespace
# Compare the cleaned parameter type with the class name
return $clean_param_type eq $class_name;
}
sub is_abstract_class {
my ($class_body) = @_;
#Regex to check for virtual <function> = 0;
if($class_body =~ /virtual\s+.*?=\s*0\s*;/s) {
return 1; #Abstract
} else {
return 0; #Not abstract
}
}
__END__
is_copy_constructor

View File

@ -94,7 +94,7 @@ else
LIBEXEC = lib
endif
TRICK_INCLUDES := -isystem${TRICK_HOME}/trick_source -isystem${TRICK_HOME}/include -isystem${TRICK_HOME}/include/trick/compat
TRICK_INCLUDES := -isystem${TRICK_HOME}/trick_source -isystem${TRICK_HOME}/include -isystem${TRICK_HOME}/include/trick/compat -I./build
TRICK_VERSIONS := -DTRICK_VER=$(TRICK_MAJOR) -DTRICK_MINOR=$(TRICK_MINOR)
export TRICK_SYSTEM_CXXFLAGS := $(TRICK_INCLUDES) $(TRICK_VERSIONS) -fpic $(UDUNITS_INCLUDES) -I${TRICK_HOME}/build

View File

@ -1,21 +1,14 @@
import math
from trick.unit_test import *
print(f"alloc_test.atwargs = {alloc_test}")
trick.sim_control_panel_set_enabled(True)
trick.exec_set_enable_freeze(True)
trick.exec_set_freeze_command(True)
def main():
trick_utest.unit_tests.enable()
trick_utest.unit_tests.set_file_name( os.getenv("TRICK_HOME") + "/trick_test/SIM_tmm_alloc_args.xml" )
trick_utest.unit_tests.set_test_name( "TMMAllocWithArgsTest" )
trick.add_read(4.0, """TRICK_EXPECT_EQ(alloc_test.atwargs.some_int, 0)""")
trick.add_read(4.0, """TRICK_EXPECT_NEAR(alloc_test.atwargs.some_double, 0, 1e-6)""")
#Use tmm_alloc_args from input file - thanks convert_swig!
alloc_test.atwargs_input_file = trick.AllocTestWithArguments.alloc(5, 7.0)
trick.stop(5.0)
if __name__ == "__main__":
main()

View File

@ -9,6 +9,14 @@ def test():
test_case = "type_AllocTestWithArguments"
alloc_test.atwargs_input_file = trick.AllocTestWithArguments.alloc(5, 7.0)
trick.add_read(4.0, """TRICK_EXPECT_EQ(alloc_test.atwargs.some_int, 0, test_case, test_suite)""")
trick.add_read(4.0, """TRICK_EXPECT_NEAR(alloc_test.atwargs.some_double, 0, 1e-6, test_case, test_suite)""")
trick.add_read(4.0, """TRICK_EXPECT_EQ(alloc_test.atwargs.some_int, 0, test_case, test_suite)""")
trick.add_read(4.0, """TRICK_EXPECT_NEAR(alloc_test.atwargs.some_double, 0, 1e-6, test_case, test_suite)""")

View File

@ -13,6 +13,7 @@ class AllocTestSimObject : public Trick::SimObject {
public:
AllocTestWithArguments* atwargs;
AllocTestWithArguments* atwargs_input_file;
AllocTestSimObject() {
@ -24,7 +25,7 @@ class AllocTestSimObject : public Trick::SimObject {
{
std::cout << "Entered init_alloc()\n";
//std::cout << TrickTypeToString<AllocTestWithArguments>::getName() << "\n";
atwargs = Trick::tmm_alloc_args<AllocTestWithArguments>(0, 1.0);
atwargs = tmm_alloc_args<AllocTestWithArguments>(0, 1.0);
std::cout << "Called tmm_alloc_args: " << atwargs << "\n";
}
};

View File

@ -0,0 +1,10 @@
#include "alloc_with_args.hh"
AllocTestWithArguments::AllocTestWithArguments(int* in_int, double& in_double, std::string in_string)
:
some_int(*in_int),
some_double(in_double)
{
std::cout << in_string << "\nn";
}

View File

@ -1,7 +1,7 @@
/********************************* TRICK HEADER *******************************
PURPOSE: ( Test tmm_alloc_args in a sim environment )
LIBRARY DEPENDENCY:
(())
((alloc_with_args.o))
*******************************************************************************/
#include <iostream>
@ -30,6 +30,20 @@ class AllocTestWithArguments {
std::cout << "in_double: " << in_double << "\n";
}
AllocTestWithArguments(int* in_int, double *in_double, std::string &a_name)
:
some_int(*in_int),
some_double(*in_double)
{
std::cout << "AllocTestWithArguments constructor with: \n";
std::cout << a_name << std::endl;
std::cout << "in_int: " << in_int << "\n";
std::cout << "in_double: " << in_double << "\n";
}
AllocTestWithArguments(int*, double&, std::string);
~AllocTestWithArguments() {
std::cout << "AllocTestWithArguments desctruct.\n";
}