Nested class improvements - Fixed inconsistency in handling C++ nested classes - sometimes they were treated as forward declarations, other times as if C nested struct was parsed. Added the nestedworkaround feature for C++ nested class handling. Document improved nested class handling. Numerous C and C++ nested struct/class/union test cases added.

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@11734 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
William S Fulton 2009-11-11 00:30:34 +00:00
commit ebed6508e4
12 changed files with 300 additions and 82 deletions

View file

@ -1,12 +1,30 @@
Version 1.3.41 (in progress)
============================
2009-11-11: wsfulton
Added the nestedworkaround feature as a way to use the full functionality of a nested class
(C++ mode only). It removes the nested class from SWIG's type information so it is as if SWIG
had never parsed the nested class. The documented nested class workarounds using a global
fake class stopped working when SWIG treated the nested class as an opaque pointer, and
this feature reverts this behaviour. The documentation has been updated with details of how
to use and implement it, see the "Nested classes" section in SWIGPlus.html.
2009-11-11: wsfulton
There were a number of C++ cases where nested classes/structs/unions were being handled
as if C code was being parsed which would oftentimes lead to uncompileable code as an
attempt was made to wrap the nested structs like it is documented for C code. Now all
nested structs/classes/unions are ignored in C++ mode, as was always documented. However,
there is an improvement as usage of nested structs/classes/unions is now always treated
as an opaque type by default, resulting in generated code that should always compile.
*** POTENTIAL INCOMPATIBILITY ***
2009-11-09: drjoe
Fix R for -fcompact and add std_map.i
2009-11-08: wsfulton
Fix inconsistency for inner structs/unions/classes. Uncompileable code was being
generated when inner struct and union declarations were used as types within
the inner struct. The inner struct/union is now treated as a forward declaration making the
Fix inconsistency for nested structs/unions/classes. Uncompileable code was being
generated when inner struct and union declarations were used as types within the
inner struct. The inner struct/union is now treated as a forward declaration making the
behaviour the same as an inner class. (C++ code), eg:
struct Outer {

View file

@ -334,7 +334,7 @@ major features include:
<p>
Currently, the only major C++ feature not supported is nested classes--a limitation
that will be removed in a future release.
that should be removed in a future release, but has some workarounds for the moment.
</p>
<p>

View file

@ -334,6 +334,7 @@ currently supported:
For example, SWIG does not support declarations such as the following
(even though this is legal C):
<p>
<div class="code">
<pre>
/* Non-conventional placement of storage specifier (extern) */
@ -347,6 +348,7 @@ void bar(Spam (Grok)(Doh));
</pre>
</div>
</p>
<p>
In practice, few (if any) C programmers actually write code like
@ -360,6 +362,7 @@ is not recommended. Even though SWIG can parse C++ class declarations,
it ignores declarations that are decoupled from their
original class definition (the declarations are parsed, but a lot of warning
messages may be generated). For example:
<p>
<div class="code">
<pre>
@ -369,11 +372,12 @@ int foo::bar(int) {
}
</pre>
</div>
</p>
</li>
<li>Certain advanced features of C++ such as nested classes
are not yet supported. Please see the section on using SWIG
with C++ for more information.
are not yet fully supported. Please see the <a href="SWIGPlus.html">C++ section</a>
for more information.
</ul>
<p>
@ -2633,6 +2637,12 @@ If you have a lot nested structure declarations, it is
advisable to double-check them after running SWIG. Although,
there is a good chance that they will work, you may have to
modify the interface file in certain cases.
</p>
<p>
Finally, note that nesting is handled differently in C++ mode,
see <a href="SWIGPlus.html#SWIGPlus_nested_classes">Nested classes</a>.
</p>
<H3><a name="SWIG_nn39"></a>5.5.8 Other things to note about structure wrapping</H3>

View file

@ -4695,37 +4695,49 @@ public:
<p>
There is limited support for nested structs and unions when wrapping C code, see <a href="SWIG.html#SWIG_nested_structs">Nested structures</a> for further details.
However, there is no nested class/struct/union support when wrapping C++ code (using the -c++ commandline option).
This may be added at a future date, however, until then some of the following workarounds can be applied.
There is some support for nested structs and unions when wrapping C code,
see <a href="SWIG.html#SWIG_nested_structs">Nested structures</a> for further details.
The added complexity of C++ compared to C means this approach does not work well for
C++ code (when using the -c++ command line option).
For C++, a nested class is treated much like an opaque pointer, so anything useful within the nested class, such as its
methods and variables, are not accessible from the target language.
True nested class support may be added to SWIG in the future, however,
until then some of the following workarounds can be applied to improve the situation.
</p>
<p>
It might be possible to use partial class information. Since
SWIG does not need the entire class specification to work, conditional
compilation can be used to comment out the problematic nested class definition, you might do this:
It might be possible to use partial class information as often you can accept that the nested class is not needed,
especially if it is not actually used in any methods you need from the target language.
Imagine you are wrapping the following <tt>Outer</tt> class which contains a nested class <tt>Inner</tt>.
The easiest thing to do is turn a blind eye to the warning that SWIG generates, or simply suppress it:
</p>
<div class="code">
<pre>
class Foo {
#pragma SWIG nowarn=SWIGWARN_PARSE_NESTED_CLASS
class Outer {
public:
#ifndef SWIG
class Bar {
public:
...
};
#endif
Foo();
~Foo();
...
class Inner {
public:
...
};
Inner getInner();
void useInner(const Inner&amp; inner);
...
};
</pre>
</div>
<p>
The next workaround assumes you cannot modify the source code as was done above and it provides a solution for methods that use nested class types.
Imagine we are wrapping the <tt>Outer</tt> class which contains a nested class <tt>Inner</tt>:
Note that if <tt>Inner</tt> can be used as an opaque type, the default wrapping approach suffices.
For example, if the nested class does not need to be created from the target language, but can be obtained via a method
call, such as the <tt>getInner()</tt> method above, the returned value can then be passed around, such as passed into the
<tt>useInner()</tt> method.
</p>
<p>
With some more effort the above situation can be improved somewhat and a nested class can be constructed and used
from the target language much like any other non-nested class. Assuming we have the <tt>Outer</tt> class in a header file:
</p>
<div class="code">
@ -4738,14 +4750,18 @@ public:
int var;
Inner(int v = 0) : var(v) {}
};
void method(Inner inner);
Inner getInner();
void useInner(const Inner&amp; inner);
};
</pre>
</div>
<p>
The following interface file works around SWIG nested class limitations by redefining the nested class as a global class.
A typedef for the compiler is also required in order for the generated wrappers to compile.
The following interface file works around the nested class limitations by redefining the nested class as a global class.
A typedef for the compiler and the <tt>nestedworkaround</tt>
<a href="Customization.html#Customization_feature_flags">feature flag</a> is also required in
order for the generated wrappers to compile. This flag simply removes all the type information from SWIG, so SWIG treats
the nested class as if it had not been parsed at all.
</p>
<div class="code">
@ -4753,9 +4769,6 @@ A typedef for the compiler is also required in order for the generated wrappers
// File : example.i
%module example
// Suppress SWIG warning
#pragma SWIG nowarn=SWIGWARN_PARSE_NESTED_CLASS
// Redefine nested class in global scope in order for SWIG to generate
// a proxy class. Only SWIG parses this definition.
class Inner {
@ -4764,24 +4777,62 @@ class Inner {
Inner(int v = 0) : var(v) {}
};
%nestedworkaround Outer::Inner;
%{
#include "outer.h"
%}
%include "outer.h"
// We've fooled SWIG into thinking that Inner is a global class, so now we need
// to trick the C++ compiler into understanding this apparent global type.
%{
// SWIG thinks that Inner is a global class, so we need to trick the C++
// compiler into understanding this so called global type.
typedef Outer::Inner Inner;
%}
</pre>
</div>
<p>
The downside to this approach is having to maintain two definitions of <tt>Inner</tt>, the real one and the one in the interface file that SWIG parses.
The downside to this approach is a more complex interface file and having to maintain two definitions of <tt>Inner</tt>,
the real one and the one in the interface file that SWIG parses.
However, the upside is that all the methods/variables in the nested class are available from the target language
as a proxy class is generated instead of treating the nested class as an opaque type.
The proxy class can be constructed from the target language and passed into any methods accepting the nested class.
Also note that the original header file is parsed unmodified.
</p>
<p>
Finally, conditional compilation can be used as a workaround to comment out nested class definitions in the actual headers,
assuming you are able to modify them.
</p>
<div class="code">
<pre>
// File outer.h
class Outer {
public:
#ifndef SWIG
class Inner {
public:
...
};
#endif
...
};
</pre>
</div>
<p>
This workaround used to be common when SWIG could not deal with nested classes particulary well.
This should just be a last resort for unusual corner cases now as SWIG can parse nested classes and even handle nested template classes fairly well.
</p>
<p>
<b>Compatibility Note:</b> SWIG-1.3.40 and earlier versions did not have the <tt>nestedworkaround</tt> feature
and the generated code resulting from parsing nested classes did not always compile.
</p>
<H2><a name="SWIGPlus_nn37"></a>6.27 A brief rant about const-correctness</H2>

View file

@ -244,6 +244,7 @@ CPP_TEST_CASES += \
naturalvar \
nested_class \
nested_comment \
nested_workaround \
newobject1 \
null_pointer \
operator_overload \

View file

@ -12,6 +12,27 @@ struct TestStruct {
int a;
};
struct OuterStructNamed {
struct InnerStructNamed {
double dd;
} inner_struct_named;
union InnerUnionNamed {
double ee;
int ff;
} inner_union_named;
};
struct OuterStructUnnamed {
struct {
double xx;
} inner_struct_unnamed;
union {
double yy;
int zz;
} inner_union_unnamed;
};
typedef struct OuterStruct {
union {

View file

@ -4,52 +4,94 @@
%inline %{
struct Outer {
typedef int Integer;
///////////////////////////////////////////
struct InnerStruct1 {
int x;
Integer x;
};
class InnerClass1 {
public:
int x;
Integer x;
};
union InnerUnion1 {
int x;
Integer x;
double y;
};
///////////////////////////////////////////
class {
public:
int a;
Integer a;
};
struct {
int b;
Integer b;
};
union {
int c;
Integer c;
double d;
};
///////////////////////////////////////////
class InnerClass2 {
public:
int x;
Integer x;
} InnerClass2Name;
struct InnerStruct2 {
int x;
Integer x;
} InnerStruct2Name;
union InnerUnion2 {
int x;
Integer x;
double y;
} InnerUnion2Name;
///////////////////////////////////////////
class {
public:
Integer x;
} InnerClass3Name;
struct {
Integer x;
} InnerStruct3Name;
union {
Integer x;
double y;
} InnerUnion3Name;
///////////////////////////////////////////
typedef class {
public:
Integer x;
} InnerClass4;
typedef struct {
Integer x;
} InnerStruct4;
typedef union {
Integer x;
double y;
} InnerUnion4;
// bug #909387 - inner declared types are treated as forward declarations
InnerStruct1* getInnerStruct1() { return 0; }
InnerClass1* getInnerClass1() { return 0; }
InnerUnion1* getInnerUnion1() { return 0; }
InnerStruct2* getInnerStruct2() { return 0; }
InnerClass2* getInnerClass2() { return 0; }
InnerUnion2* getInnerUnion2() { return 0; }
InnerStruct4* getInnerStruct4() { return 0; }
InnerClass4* getInnerClass4() { return 0; }
InnerUnion4* getInnerUnion4() { return 0; }
};
%}

View file

@ -0,0 +1,38 @@
%module nested_workaround
// Similar to "Nested classes" documentation example.
class Inner {
int val;
public:
Inner(int v = 0) : val(v) {}
void setValue(int v) { val = v; }
int getValue() const { return val; }
};
%nestedworkaround Outer::Inner;
%inline %{
class Outer {
public:
class Inner {
int val;
public:
Inner(int v = 0) : val(v) {}
void setValue(int v) { val = v; }
int getValue() const { return val; }
};
Inner createInner(int v) const { return Inner(v); }
int getInnerValue(const Inner& i) const { return i.getValue(); }
Inner doubleInnerValue(Inner inner) {
inner.setValue(inner.getValue() * 2);
return inner;
}
};
%}
// We've fooled SWIG into thinking that Inner is a global class, so now we need
// to trick the C++ compiler into understanding this apparent global type.
%{
typedef Outer::Inner Inner;
%}

View file

@ -0,0 +1,13 @@
from nested_workaround import *
inner = Inner(5)
outer = Outer()
newInner = outer.doubleInnerValue(inner)
if newInner.getValue() != 10:
raise RuntimeError
outer = Outer()
inner = outer.createInner(3)
newInner = outer.doubleInnerValue(inner)
if outer.getInnerValue(newInner) != 6:
raise RuntimeError

View file

@ -136,6 +136,11 @@
#define %nocallback %feature("callback","0")
#define %clearcallback %feature("callback","")
/* the %nestedworkaround directive */
#define %nestedworkaround %feature("nestedworkaround")
#define %nonestedworkaround %feature("nestedworkaround","0")
#define %clearnestedworkaround %feature("nestedworkaround","")
/* the %fastdispatch directive */
#define %fastdispatch %feature("fastdispatch")
#define %nofastdispatch %feature("fastdispatch","0")

View file

@ -4374,18 +4374,33 @@ cpp_protection_decl : PUBLIC COLON {
by SWIG or this whole thing is going to puke.
---------------------------------------------------------------------- */
/* A struct sname { } id; declaration */
/* struct sname { } id; or struct sname { }; declaration */
cpp_nested : storage_class cpptype ID LBRACE { cparse_start_line = cparse_line; skip_balanced('{','}');
} nested_decl SEMI {
$$ = 0;
if (cplus_mode == CPLUS_PUBLIC) {
if ($6.id && strcmp($2, "class") != 0) {
if (cparse_cplusplus) {
/* Treat the nested class/struct/union as a forward declaration until a proper nested class solution is implemented */
$$ = new_node("classforward");
Setfile($$,cparse_file);
Setline($$,cparse_line);
Setattr($$,"kind",$2);
Setattr($$,"name",$3);
Setattr($$,"sym:weak", "1");
add_symbols($$);
if (GetFlag($$, "feature:nestedworkaround")) {
Swig_symbol_remove($$);
$$ = 0;
} else {
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line, "Nested %s not currently supported (%s ignored).\n", $2, $3);
}
} else if ($6.id) {
/* Generate some code for a new struct */
Nested *n = (Nested *) malloc(sizeof(Nested));
n->code = NewStringEmpty();
Printv(n->code, "typedef ", $2, " ",
Char(scanner_ccode), " $classname_", $6.id, ";\n", NIL);
Printv(n->code, "typedef ", $2, " ", Char(scanner_ccode), " $classname_", $6.id, ";\n", NIL);
n->name = Swig_copy_string($6.id);
n->line = cparse_start_line;
n->type = NewStringEmpty();
@ -4394,50 +4409,53 @@ cpp_nested : storage_class cpptype ID LBRACE { cparse_start_line = cparse_line
SwigType_push(n->type, $6.type);
n->next = 0;
add_nested(n);
} else {
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line, "Nested %s not currently supported (%s ignored).\n", $2, $3);
/* For now, just treat the nested class/struct/union as a forward
* declaration (SF bug #909387). */
$$ = new_node("classforward");
Setfile($$,cparse_file);
Setline($$,cparse_line);
Setattr($$,"kind",$2);
Setattr($$,"name",$3);
Setattr($$,"sym:weak", "1");
add_symbols($$);
}
}
}
/* A struct { } id; declaration */
/* struct { } id; or struct { }; declaration */
| storage_class cpptype LBRACE { cparse_start_line = cparse_line; skip_balanced('{','}');
} nested_decl SEMI {
$$ = 0;
if (cplus_mode == CPLUS_PUBLIC) {
if (strcmp($2,"class") == 0) {
if ($5.id)
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line,"Nested class not currently supported (%s ignored)\n", $5.id);
else
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line,"Nested class not currently supported (ignored)\n");
} else if ($5.id) {
/* Generate some code for a new class */
Nested *n = (Nested *) malloc(sizeof(Nested));
n->code = NewStringEmpty();
Printv(n->code, "typedef ", $2, " " ,
Char(scanner_ccode), " $classname_", $5.id, ";\n",NIL);
n->name = Swig_copy_string($5.id);
n->line = cparse_start_line;
n->type = NewStringEmpty();
n->kind = $2;
n->unnamed = 1;
SwigType_push(n->type,$5.type);
n->next = 0;
add_nested(n);
if ($5.id) {
if (cparse_cplusplus) {
/* Treat the nested class/struct/union as a forward declaration until a proper nested class solution is implemented */
$$ = new_node("classforward");
Setfile($$,cparse_file);
Setline($$,cparse_line);
Setattr($$,"kind",$2);
Setattr($$,"name",$5.id);
Setattr($$,"sym:weak", "1");
add_symbols($$);
if (GetFlag($$, "feature:nestedworkaround")) {
Swig_symbol_remove($$);
$$ = 0;
} else {
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line,"Nested %s not currently supported (%s ignored)\n", $2, $5.id);
}
} else {
/* Generate some code for a new struct */
Nested *n = (Nested *) malloc(sizeof(Nested));
n->code = NewStringEmpty();
Printv(n->code, "typedef ", $2, " " , Char(scanner_ccode), " $classname_", $5.id, ";\n",NIL);
n->name = Swig_copy_string($5.id);
n->line = cparse_start_line;
n->type = NewStringEmpty();
n->kind = $2;
n->unnamed = 1;
SwigType_push(n->type,$5.type);
n->next = 0;
add_nested(n);
}
} else {
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line, "Nested %s not currently supported (ignored).\n", $2);
}
}
}
/* A 'class name : base_list { };' declaration, always ignored */
/*****
This fixes derived_nested.i, but it adds one shift/reduce. Anyway,
@ -4445,12 +4463,12 @@ cpp_nested : storage_class cpptype ID LBRACE { cparse_start_line = cparse_line
*****/
| storage_class cpptype idcolon COLON base_list LBRACE { cparse_start_line = cparse_line; skip_balanced('{','}');
} SEMI {
Printf(stdout, "cpp_nested (c)\n");
$$ = 0;
if (cplus_mode == CPLUS_PUBLIC) {
Swig_warning(WARN_PARSE_NESTED_CLASS, cparse_file, cparse_line,"Nested %s not currently supported (%s ignored)\n", $2, $3);
}
}
/* This unfortunately introduces 4 shift/reduce conflicts, so instead the somewhat hacky nested_template is used for ignore nested template classes. */
/*
| TEMPLATE LESSTHAN template_parms GREATERTHAN cpptype idcolon LBRACE { cparse_start_line = cparse_line; skip_balanced('{','}');

View file

@ -1331,7 +1331,8 @@ void Swig_symbol_remove(Node *n) {
Setattr(symtab, symname, symnext);
fixovername = symnext; /* fix as symbol to remove is at head of linked list */
} else {
Delattr(symtab, symname);
if (symname)
Delattr(symtab, symname);
}
}
if (symnext) {