On 16/05/12 16:37, Bryan Jacobs wrote:
> On Wed, 16 May 2012 15:46:38 +0100
> William S Fulton<wsf@...> wrote:
>> On 16/05/12 15:42, William S Fulton wrote:
>>> On 12/01/12 18:08, Bryan Jacobs wrote:
>>>> Dear swig-users,
>>>> I've been bothered by an issue with the SWIG C++ parser for a
>>>> little over a year now. I've finally gotten around to distilling
>>>> it to a reproduction case, attached. Set the PYTHONDIR in the
>>>> given Makefile to your Python includes directory and run "make" to
>>>> build the (very simple) project.
>>>> The issue is that SWIG doesn't properly understand some typedef
>>>> expansions; it incorrectly reports an arguments mismatch when you
>>>> pass a valid, matching type.
>>>> In the attached document, "SubType" and "SubType2" are functionally
>>>> equivalent. SubType<T> has a constructor accepting an instance of
>>>> T, and SubType2<T> has a constructor accepting an instance of
>>>> T::WrappedType. In English, SubType is templated on its
>>>> constructor's argument type, whereas SubType2 is templated on a
>>>> type containing its constructor's argument type.
>>>> Wrapper<T> defines "WrappedType" to be T. Thus,
>>>> Wrapper<T>::WrappedType is exactly the same as a bare "T" for all
>>>> The given "instance.cpp" file generates an executable which
>>>> demonstrates that this is valid C++, by constructing and using
>>>> both a SubType<Param> and SubType2<Wrapper<Param> >. As expected,
>>>> they are identical in functionality.
>>>> However, when invoking the logically equivalent code from SWIG's
>>>> Python module (see "run.py"), we get this:
>>>> TypeError: in method 'new_st_instance2', argument 1 of type
>>>> Note that it says "Wrapper<Param>::WrappedType" there.
>>>> Wrapper<Param>::WrappedType is the same type as "Param". We are
>>>> trying to pass an object which is a SWIG proxy of a Param.
>>>> I would appreciate any guidance on this. From where I sit, it looks
>>>> like a bug: the SWIG parser isn't unfolding the typedef properly,
>>>> so SWIG_IsOK rejects a correct call.
>>> Bryan, you havn't got SWIG to parse Wrapper<> so it'll never know
>>> what it is. I've modified your swig.i file to:
>>> %module generated
>>> #include "param.h"
>>> #include "subtype.h"
>>> #include "subtype2.h"
>>> #include "wrapper.h"
>>> %include "std_string.i"
>>> %include "param.h"
>>> %include "subtype.h"
>>> %include "subtype2.h"
>>> %include "wrapper.h"
>>> %template(wrapper_param) Wrapper<Param>;
>>> %template(st_instance) SubType<Param>;
>>> %template(st_instance2) SubType2<Wrapper<Param> >;
>>> Now I get:
>>> $ python run.py
>>> hello Python world
>>> hello Python world
>>> I used swig-2.0.6.
>> Note a possibly better solution is to replace:
>> %template(wrapper_param) Wrapper<Param>;
>> %template() Wrapper<Param>;
>> Also, note that swig-2.0.5 had some fixes in this area of typedef
>> resolution for templates, so it may not have worked in swig-2.0.4
>> which was current when you posted this, you might want to check.
> Thank you for your reply.
> I've confirmed that, even in swig-2.0.4, template-instantiating the
> Wrapper<Param> type resolves the issue.
> However, if you take a close look at the example, you should see that
> there's some confusion between static and dynamic information: the
> "SubType2" class does not hold an instance of Wrapper<Param>, nor do
> any of its methods take a non-reference argument of Wrapper<Param>, nor
> do they return one.
> Using the C++ compiler, no template instance of Wrapper<Param> is
> emitted, as none is needed - it has no run-time existence.
> In SWIG, analogously, no glue function which expects a Wrapper<Param>
> or operates on one is created by the "%template()
> SubType2<Wrapper<Param> >;" line, so I wouldn't expect to have to do
> "%template() Wrapper<Param>;".
> The only thing the concrete type is used for is extracting the
> (statically available!) "WrappedType" typedef. So why must I create a
> run-time representation of a type I don't need, just to inform SWIG of
> the compile-time information?
> You can confirm this for yourself by looking at the symbols in the
> "run-cpp" binary and grepping for "Wrapper":
> 0000000000400b08 W _ZN8SubType2I7WrapperI5ParamEE5greetEv
> 0000000000400ae2 W _ZN8SubType2I7WrapperI5ParamEEC1ES1_
> 0000000000400ae2 W _ZN8SubType2I7WrapperI5ParamEEC2ES1_
> 0000000000400a88 W _ZN8SubType2I7WrapperI5ParamEED1Ev
> 0000000000400a88 W _ZN8SubType2I7WrapperI5ParamEED2Ev
> The template instances created were SubType<Wrapper<Param> > and
> SubType<Param>, but NOT Wrapper<Param>.
> If we add a constructor to Wrapper, **and add code which
> requires a template-instantiation of Wrapper<Param>**, such as putting
> a "TWrap x" member into SubType2, we see the below appear:
> 0000000000400b32 W _ZN7WrapperI5ParamEC1Ev
> 0000000000400b32 W _ZN7WrapperI5ParamEC2Ev
> ... but that's not part of the example I sent. If you don't have code
> using a concrete Wrapper<Param>, you don't get any generated instance,
> which is as it should be per the C++ standards. If you have code which
> uses a concrete Wrapper<Param>, but it has no contents (no static
> variables, no methods,&c), you still don't get an instance.
> That's why I find it a little counter-intuitive that SWIG requires a
> %template line for something it's not going to be manipulating. I would
> have expected that the %include line would be sufficient, and (as of
> 2.0.4), it is not, even when the type in question is *not* passed
> across the Python-C++ boundary, and doesn't have any of its methods
> called via SWIG-generated glue...
> I've attached an example which demonstrates this. Take a look at what
> happens to the emitted "run-cpp" when the marked line in "subtype2.h" is
> Is this possible to fix? I really don't want to have to emit SWIG glue
> for types we're not using (and should not be exposing) in Python. If it
> is too difficult to fix, it would still be nice to have some way to say
> "please don't create glue methods which accept types you don't
> understand". I mean, how could I pass a "Wrapper<Param>::WrappedType"
> into the Python code, when the Param-proxy I have is rejected by the
> type checker, and Param is the only type in "generated.py", the
> SWIG-produced Python library?
All "%template()" does is add the provided type into the SWIG type
system and hence the normal typedef resolution will then work. If it
isn't provided then any use of this type will then behave like any other
type for which SWIG has not parsed. Personally, I would like to see an
option in SWIG where wrappers for any unknown types are warned about.
This would also help what you are requesting where with a combination of
-Werror these wrappers would not be generated until you put in a %ignore.
Adding "%template()" is a manual step which I agree probably ought to be
done automatically, or at least an option provided to do it
automatically instead of manually. Originally %template was needed to
provide a user friendly name for using the C++ template classes which
was later modified to accept no name to add the type into the type
system as support for templates was improved. I think it was done as an
initial manual workaround until the automatic approach was implemented.
Patches to implement this are welcome.