Discussion:
[Swig-devel] Unexpected Behavior Of %feature Directive
Eric Ehlers
2015-08-20 09:37:40 UTC
Permalink
Hello,

I have encountered some behavior of SWIG which I do not expect and I
would be grateful if somebody could help me to understand it.

In a nutshell, if I put lines such as this at the top of each of my xxx.i interface files:

%feature("my_feature", "feature_value_aaa");

Then in my parse tree, under the include node for interface file file1.i, I see
the value of the feature from the top of file2.i, even though SWIG has not yet
started parsing file2.i.

Here is a complete and minimal example:

demo.i:
=======

%module demo

%include foo.i
%include bar.i

foo.i:
======

%feature("my_feature", "feature_value_foo");

class Foo {
Foo();
};

bar.i:
=======

%feature("my_feature", "feature_value_bar");

class Bar {
Bar();
};

I run it using the binary release of swigwin-3.0.7, like this:

swig -debug-top 4 -c++ demo.i

Here is the relevant subset of the output, the final part:

+++ include ----------------------------------------
| name - "demo.i"
| module - "demo"
| options - 0xa8cac8

+++ module ----------------------------------------
| name - "demo"
|
+++ include ----------------------------------------
| name - "foo.i"

+++ class ----------------------------------------
| feature:my_feature - "feature_value_foo"
| classtype - "Foo"
| name - "Foo"
| sym:symtab - 0x760498
| symtab - 0xa8cd08
| allows_typedef - "1"
| typepass:visit - "1"
| allocate:visit - "1"
| kind - "class"
| sym:name - "Foo"
| allocate:has_constructor - "1"
| allocate:copy_constructor - "1"
| allocate:default_destructor - "1"
| has_destructor - "1"
| allocate:destructor - "1"
| classtypeobj - "Foo"
| module - 0xa8cb38
| sym:overname - "__SWIG_0"
| typescope - 0xa8d318

+++ constructor ----------------------------------------
| ismember - "1"
| name - "Foo"
| decl - "f()."
| access - "private"
| feature:new - "1"
| feature:my_feature - "feature_value_foo"
| sym:symtab - 0xa8cd08
|
+++ access ----------------------------------------
| kind - "public"
|
+++ access ----------------------------------------
| kind - "public"
|
+++ destructor ----------------------------------------
| feature:my_feature - "feature_value_bar"
| name - "~Foo"
| sym:symtab - 0xa8cd08
| view - "destructorDeclaration"
| sym:name - "~Foo"
| wrap:parms - Foo *self
| decl - "f()."
| tmap:out - ""
| access - "public"
| wrap:action - "delete arg1;"
| wrap:name - "_wrap_delete_Foo"
| tmap:out:noblock - "1"
| sym:overname - "__SWIG_0"
|
+++ include ----------------------------------------
| name - "bar.i"

+++ class ----------------------------------------
| feature:my_feature - "feature_value_bar"
| classtype - "Bar"
| name - "Bar"
| sym:symtab - 0x760498
| symtab - 0xa8cfd8
| allows_typedef - "1"
| typepass:visit - "1"
| allocate:visit - "1"
| kind - "class"
| sym:name - "Bar"
| allocate:has_constructor - "1"
| allocate:copy_constructor - "1"
| allocate:default_destructor - "1"
| has_destructor - "1"
| allocate:destructor - "1"
| classtypeobj - "Bar"
| module - 0xa8cb38
| sym:overname - "__SWIG_0"
| typescope - 0xa8d428

+++ constructor ----------------------------------------
| ismember - "1"
| name - "Bar"
| decl - "f()."
| access - "private"
| feature:new - "1"
| feature:my_feature - "feature_value_bar"
| sym:symtab - 0xa8cfd8
|
+++ access ----------------------------------------
| kind - "public"
|
+++ access ----------------------------------------
| kind - "public"
|
+++ destructor ----------------------------------------
| feature:my_feature - "feature_value_bar"
| name - "~Bar"
| sym:symtab - 0xa8cfd8
| view - "destructorDeclaration"
| sym:name - "~Bar"
| wrap:parms - Bar *self
| decl - "f()."
| tmap:out - ""
| access - "public"
| wrap:action - "delete arg1;"
| wrap:name - "_wrap_delete_Bar"
| tmap:out:noblock - "1"
| sym:overname - "__SWIG_0"
|

You can see that the node for ~Foo has the property
"feature_value_bar", where I would expect (or want) it to have the
property "feature_value_foo", since at that point I have not yet even
hit file bar.i where "feature_value_bar" is defined.

What am I doing wrong?

I have found that if I put this at the bottom of every xxx.i file...

%feature("my_feature", "");

...then in the example above the ~Foo node does not contain the
feature at all which is an improvement but still not ideal for my
situation.

More generally, what I really want is to be able to set some parameters
at the top of each of my xxx.i files, and then to write some code which
extracts those parameters within my class:

class MY_CLASS : public Language { /* ... */ }

I don't really care whether I use %feature or something else, I just
want to extract some text every time I hit a new xxx.i file. So far I
can not figure out where in MY_CLASS to do this. Underneath a given
include node, it is impossible for me to know which will be the first
sub-node to contain the feature, it might be a namespace, function,
constructor, etc., it varies depending on what C++ code the user
chooses to put into the xxx.i file. So far the only solution that I
have found is to override all of the relevant member functions of
Language and to check for the feature in all of them e.g.

namespaceDeclaration()
functionHandler()
constructorDeclaration()
etc.

Is there a way that I can enter arbitrary text at the top of the xxx.i
file and have it attached to the corresponding include node? Then I
could always extract it within includeDirective().

I would be grateful for some help!

Kind Regards,
Eric Ehlers

------------------------------------------------------------------------------
William S Fulton
2015-08-23 09:42:29 UTC
Permalink
Post by Eric Ehlers
Hello,
I have encountered some behavior of SWIG which I do not expect and I
would be grateful if somebody could help me to understand it.
%feature("my_feature", "feature_value_aaa");
<snip>
|
Post by Eric Ehlers
You can see that the node for ~Foo has the property
"feature_value_bar", where I would expect (or want) it to have the
property "feature_value_foo", since at that point I have not yet even
hit file bar.i where "feature_value_bar" is defined.
What am I doing wrong?
Nothing really! However, if you change your code and add in an
explicit destructor, it will behave as you expect. This is a side
effect of the way that implicit destructors are handled. SWIG uses
mulitple passes and the destructors are added in a later pass and the
features that are attached to the implicit destructor are those
'active' at the end of the first pass, ie after parsing everything.
The last relevant feature you have in your code is

%feature("my_feature", "feature_value_bar");

Hence this gets attached to the implicitly added destructors and constructors.

You are using global features which rely on the order of the code and
%feature declarations. You ought to consider using more explicit
features, which would then correctly attach to the implicitly added
destructor. So use:

%feature("my_feature", "feature_value_foo") Foo::~Foo;
Post by Eric Ehlers
I have found that if I put this at the bottom of every xxx.i file...
%feature("my_feature", "");
...then in the example above the ~Foo node does not contain the
feature at all which is an improvement but still not ideal for my
situation.
The above clears the feature and hence is not active for the
destructor which is implicitly added after the first pass.
Post by Eric Ehlers
More generally, what I really want is to be able to set some parameters
at the top of each of my xxx.i files, and then to write some code which
class MY_CLASS : public Language { /* ... */ }
I don't really care whether I use %feature or something else, I just
want to extract some text every time I hit a new xxx.i file. So far I
can not figure out where in MY_CLASS to do this. Underneath a given
include node, it is impossible for me to know which will be the first
sub-node to contain the feature, it might be a namespace, function,
constructor, etc., it varies depending on what C++ code the user
chooses to put into the xxx.i file. So far the only solution that I
have found is to override all of the relevant member functions of
Language and to check for the feature in all of them e.g.
namespaceDeclaration()
functionHandler()
constructorDeclaration()
etc.
Is there a way that I can enter arbitrary text at the top of the xxx.i
file and have it attached to the corresponding include node? Then I
could always extract it within includeDirective().
Try %pragma or you could use %insert to insert some code into some
given section of the generated code. This won't attach it to the node,
but your MY_CLASS should be able to pick it up in a defined place.

Maybe this is another approach you could consider:

%include(mkey=mvalue) foo.i

Language::includeDirective() can then pick up "mykey=myvalue".

William

------------------------------------------------------------------------------
Continue reading on narkive:
Loading...