Skip to content

Commit

Permalink
SWIG 4 Template Directive Changes (#1741)
Browse files Browse the repository at this point in the history
* Updated convert_swig to account for template directive changes in SWIG 4

* Fixed merge issues

* Merge cleanup
  • Loading branch information
Pherring04 authored Jul 23, 2024
1 parent 7b4253d commit a9aa708
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 55 deletions.
11 changes: 7 additions & 4 deletions docs/developer_docs/DesConvertSwig.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,13 @@ Add the <b>class_string</b> to the SWIG interface text.
1. While there's class content text remaining to be processed,
repeatedly search for data members that match : <b>template_name '<'</b> <i>template-params</i> <b>'>' name ;</b>
For each match, create a SWIG %template directive to create an instantiation
of the specific templated type used by the data member. Add the
SWIG %template directive to the templated typedefs string that
Otherwise append whatever wasn't matched in process contents to
the SWIG interface text.
of the specific templated type used by the data member. Due to changes in SWIG 4,
template directives must be specified before their respective data members. As such,
the template directives are inserted immediately prior to the class definition.
SWIG does not resolve namespaces in these directives the same as C++ does
(it will only check local scope, not global). To account for this,
if the directive specifies a template outside the local namespace, convert_swig will
escape the current namespace in the format : } template_directive namespace name {

## process_typedef_struct

Expand Down
96 changes: 51 additions & 45 deletions libexec/trick/convert_swig
Original file line number Diff line number Diff line change
Expand Up @@ -617,17 +617,15 @@ sub process_class($$$$$) {
\s*[\{\:]$
/sx or die "Internal error" ;
$class_name = $1 ;

my $class_content;
$class_content .= $class_string ;
my $my_class_contents = $class_string ;

if ( $class_string !~ /\{$/ ) {
$$contents_ref =~ s/^(.*?\s*\{)//s ;
$class_content .= $1 ;
$my_class_contents .= $1 ;
}

# Add _swig_setattr_nondynamic_instance_variable function for raising AttributeError for improper class attribute assingment in input processor
$class_content .= "\n#if SWIG_VERSION > 0x040000\n\%pythoncode \%{\n __setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n\%}\n#endif\n" ;
$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 , "{}") ;

Expand All @@ -639,20 +637,11 @@ sub process_class($$$$$) {

#print "*** extracted = $extracted ***\n" ;
#print "*** contents = $$contents_ref ***\n" ;
my $save_namespace_content;

if ( $curr_namespace ne "" ) {
my @split_namespaces = split "::", $curr_namespace;
my $sanitized_namespace = $split_namespaces[-1] ;
my @namespace_split = split /namespace\s*$sanitized_namespace/, $$new_contents_ref;
$save_namespace_content = 'namespace ' . $sanitized_namespace . $namespace_split[-1];
$$new_contents_ref = join('namespace ' . $sanitized_namespace, @namespace_split[0 .. $#namespace_split-1]);
}

# SWIG doesn't like "const static". Change it to "static const"
$extracted =~ s/const\s+static/static const/g ;

my $isSwigExcludeBlock = 0;
my $isSwigExcludeBlock = 0 ;

# templated variables need to be declared with the SWIG %template directive.
# This loop looks for any templated variables and creates the %template lines.
Expand All @@ -662,15 +651,25 @@ sub process_class($$$$$) {

if ( $non_var ne "" ) {
#print "*** non_var = $non_var ***\n" ;
$class_content .= $non_var ;
$my_class_contents .= $non_var ;
my $ifndefSwig = $non_var;
if ($isSwigExcludeBlock == 0) {
if ($ifndefSwig =~ /(?:ifndef\s*SWIG|if\s*!\s*defined\s*\(\s*SWIG\s*\))/ ) {
$isSwigExcludeBlock = 1;
my $moreNonVar = 1 ;
# search for all instances of #ifndef SWIG, #if !defined(SWIG), and #endif prior to template variable
# update $isSwigExcludeBlock to the last instance*
# exit when no match is found
# * this script does not track preprocessor scope, so any #endif will set $isSwigExcludeBlock to 0
# in other words we don't support SWIGing nested preprocessor if statements, use at your peril
while ($moreNonVar == 1) {
if ($ifndefSwig =~ s/(#\s*ifndef\s*SWIG)|(#\s*if\s*!\s*defined\s*\(\s*SWIG\s*\))|(#\s*endif\s*)// ) {
if($1 ne "" or $2 ne "") {
$isSwigExcludeBlock = 1 ;
}
elsif($3 ne "") {
$isSwigExcludeBlock = 0 ;
}
}
} else {
if ($ifndefSwig =~ /endif/ ) {
$isSwigExcludeBlock = 1;
else {
$moreNonVar = 0 ;
}
}
}
Expand All @@ -681,6 +680,7 @@ sub process_class($$$$$) {
$template_var_def_str =~ /(.*?>)\s*([_A-Za-z]\w*).*?;/s ;
my ($template_full_type) = $1 ;
my ($var_name) = $2 ;
$my_class_contents .= $template_var_def_str ;

$template_full_type =~ /([_A-Za-z][:\w]*)\s*</ ;
my ($template_type) = $1 ;
Expand All @@ -703,47 +703,53 @@ sub process_class($$$$$) {
my $identifier = "${sanitized_namespace}${class_name}_${var_name}" ;
my $trick_swig_template = "TRICK_SWIG_TEMPLATE_$identifier" ;

my $typedef;
# Insert template directive immediately before intsance
# This is required as of SWIG 4
my $typedef = "\n#ifndef $trick_swig_template\n" ;
$typedef .= "#define $trick_swig_template\n" ;
$typedef .= "\%template($identifier) $template_full_type;\n" ;
$typedef .= "#endif\n" ;

# SWIG namespace resolution for template directives starts at the local space
# Therefore, if the type is qualified, assume it's fully qualified and put the
# %template directive in the global namespace by escaping the current namespace
if ($curr_namespace ne "") {
my $in_same_namespace = 1 ;
if ($template_full_type =~ /^\w*(::)\w+</) {
$in_same_namespace = 0 ;
}
if ($in_same_namespace eq 0) {
$curr_namespace =~ /(.*)::/ ;
$typedef = "\n}" . $typedef . "namespace " . $1 . " {" ;
}
}

if ($isSwigExcludeBlock == 0) {
$typedef = "#ifndef $trick_swig_template\n" ;
$typedef .= "#define $trick_swig_template\n";
if ($qualified) {
$typedef .= "\%template($identifier) $template_full_type;\n" ;
} else {
if ( $curr_namespace ne "" ) {
my $cppns = substr $curr_namespace, 0, -2;
$typedef .= "namespace $cppns {\n";
$typedef .= "\%template($identifier) $template_full_type;\n}\n" ;
} else {
$typedef .= "\%template($identifier) $template_full_type;\n" ;
}
}
$typedef .= "#endif\n\n" ;
$$new_contents_ref .= $typedef ;
$template_typedefs .= $typedef ;
}

$processed_templates{$template_type_no_sp} = 1 ;
}
}
$class_content .= $template_var_def_str ;
}
}
}
#print "*** unprocessed extracted = $extracted ***\n" ;

push @$class_names_ref , "$curr_namespace$class_name" ;

# write out the templated variable declaration lines found in this class.
$$new_contents_ref .= $template_typedefs."\n" ;

$$new_contents_ref .= $my_class_contents ;
# write the class contents and semicolon to ensure any template declarations below are after the semicolon.
$class_content .= $extracted . ";\n" ;
$$new_contents_ref .= $extracted . ";\n" ;

my $c_ = "$curr_namespace$class_name" ;
$c_ =~ s/\:/_/g ;
# Add a #define line that signals that this class has been processed by swig. Classes excluded in #if 0 blocks will
# not have this #define defined.
$class_content .= "#define TRICK_SWIG_DEFINED_$c_" ;
if ( $save_namespace_content ne "" ) {
$$new_contents_ref .= $save_namespace_content;
}
$$new_contents_ref .= $class_content;
$$new_contents_ref .= "#define TRICK_SWIG_DEFINED_$c_" ;
}

## ================================================================================
Expand Down
18 changes: 18 additions & 0 deletions test/SIM_test_ip/RUN_test/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,24 @@ def main():
TRICK_EXPECT_TRUE( test_so.test_true(), test_suite , "boolean function return" )
TRICK_EXPECT_FALSE( test_so.test_false(), test_suite , "boolean function return" )

######################################################################################################################

test_suite = "SWIG Templates"

test_so.obj.class_no_ns.tnns.x = 1
test_so.obj.class_no_ns.tns.y = 2
test_so.obj.class_ns.tnns.x = 3
test_so.obj.class_ns.tns.y = 4
test_so.obj.foo1.bar.z = 5
test_so.obj.foo2.bar.z = 6

TRICK_EXPECT_EQ( test_so.obj.class_no_ns.tnns.x, 1, test_suite , "template member access" )
TRICK_EXPECT_EQ( test_so.obj.class_no_ns.tns.y, 2, test_suite , "template member access" )
TRICK_EXPECT_EQ( test_so.obj.class_ns.tnns.x, 3, test_suite , "template member access" )
TRICK_EXPECT_EQ( test_so.obj.class_ns.tns.y, 4, test_suite , "template member access" )
TRICK_EXPECT_EQ( test_so.obj.foo1.bar.z, 5, test_suite , "template member access" )
TRICK_EXPECT_EQ( test_so.obj.foo2.bar.z, 6, test_suite , "template member access" )

######################################################################################################################

if __name__ == "__main__":
Expand Down
8 changes: 8 additions & 0 deletions test/SIM_test_ip/models/test_ip/include/ClassOfEverything.hh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ LIBRARY DEPENDENCY:
#include "test_ip/include/NoICG.hh"
#include "exclude_me/include/exclude_me.hh"
#include "test_ip/include/Namespace_tests.hh"
#include "test_ip/include/TemplateTest.hh"
#include "test_ip/include/IfndefSwigTest.hh"

/** @class Ball
@brief ball in C++
Expand Down Expand Up @@ -441,6 +443,12 @@ class ClassOfEverything {
std::map < std::string , int > msi ;
std::list < std::string > ls ;

ClassNoNS class_no_ns ;
NS2::ClassNS class_ns ;

a::Foo foo1;
b::Foo2 foo2;

private:
ClassOfEverything (const ClassOfEverything &);
ClassOfEverything & operator= (const ClassOfEverything &);
Expand Down
35 changes: 35 additions & 0 deletions test/SIM_test_ip/models/test_ip/include/FooB.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/********************************* TRICK HEADER *******************************
PURPOSE:
()
LIBRARY DEPENDENCY:
()
PROGRAMMERS:
(((Your Name) (Company Name) (Date) (Trick tutorial)))
*******************************************************************************/

#ifndef FOOB_HH_
#define FOOB_HH_

namespace FooNamespace
{

// RestartableStateIntegrator
template<int T>
class FooA
{
public:

int A = T;
};

// RestartableFirstOrderODEIntegrator
template<int T>
class FooB : public FooA<3>
{
public:
int B = T;
};

}

#endif
36 changes: 36 additions & 0 deletions test/SIM_test_ip/models/test_ip/include/FooD.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/********************************* TRICK HEADER *******************************
PURPOSE:
()
LIBRARY DEPENDENCY:
()
PROGRAMMERS:
(((Your Name) (Company Name) (Date) (Trick tutorial)))
*******************************************************************************/

#ifndef FOOD_HH_
#define FOOD_HH_

#include "FooB.hh"

namespace FooNamespace
{

// RestartableSecondOrderODEIntegrator
class FooC : public FooA<1>
{
public:
int C;
};

// RestartableSimpleSecondOrderODEIntegrator
template<int T>
class FooD : public FooC
{
public:
int D = T;
};


}

#endif
62 changes: 62 additions & 0 deletions test/SIM_test_ip/models/test_ip/include/IfndefSwigTest.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/********************************* TRICK HEADER *******************************
PURPOSE:
()
LIBRARY DEPENDENCY:
()
PROGRAMMERS:
(((Your Name) (Company Name) (Date) (Trick tutorial)))
*******************************************************************************/

#ifndef FOO_HH_
#define FOO_HH_

#include "FooB.hh"
#include "FooD.hh"

namespace FooNamespace
{

// RestartableScalarFirstOrderODEIntegrator
class FooContB {
public:
FooContB() : d(12.0) {}

// RestartableFirstOrderODEIntegrator
FooB<2> fooB;

FooB<2> fooB2;

double d;
};


// RestartableT3SecondOrderODEIntegrator
class FooContD {
public:
FooContD() : d(12.0) {}

// RestartableSimpleSecondOrderODEIntegrator
#ifndef TESTING_SWIG
# ifndef SWIG
// THIS SHOULD PREVENT SWIG FROM MAKING ANY TEMPLATE REFERENCES TO EXCLUDED FooD TYPE
FooD<1> fooD;

FooD<2> fooD2;
#endif
#endif

int spacer;

#if ! defined ( SWIG )
// THIS SHOULD PREVENT SWIG FROM MAKING ANY TEMPLATE REFERENCES TO EXCLUDED FooD TYPE
FooD<3> fooD3;

FooD<4> fooD4;
#endif

double d;
};

}

#endif
Loading

0 comments on commit a9aa708

Please sign in to comment.