From: Sébastien H. <seb...@ge...> - 2009-09-16 08:57:01
|
Hi, It seems that there is a bug while calling a C++ static method with arguments in Perl binding, e.g.: -------------------------C++ code---------------------------------------------------- > class Foo { > public: > static void bar(int a, int b); > }; > > -------------------------Perl code---------------------------------------------------- > Perl: > my $a = 1; > my $b = 2; > Foo->bar($a,$b); > > I have an error like this : > RuntimeError Usage: Foo_bar(a,b); > ----------------------------------------------------------------------------------------- This is because the wrapper doesn't handle the *$self* argument passed silently by Perl. The wrapper.cpp tests the arguments like this: -------------------------generated wrapper.cpp---------------------------------------------------- > if ((items < 2) || (items > 2)) { > SWIG_croak("Usage: Foo_bar(a,b);"); > } > ----------------------------------------------------------------------------------------- But there is 3 arguments, not 2!: $self, $a and $b A fix may be something like adding a *"shift items*" to the wrapper to remove *$self* (not tested), before the number arguments test. I dirtily "fixed" this in my project wrapper.cpp like this: -------------------------generated wrapper.cpp---------------------------------------------------- *Original code generating errors:* XS(_wrap_SchemaValidator_run) { { std::string *arg1 = 0 ; std::string *arg2 = 0 ; int res1 = SWIG_OLDOBJ ; int res2 = SWIG_OLDOBJ ; int argvi = 0; bool result; dXSARGS; *if ((items < 2) || (items > 2))* { SWIG_croak("Usage: SchemaValidator_run(xml_file_pathname,xml_schema_file_pathname);"); } { std::string *ptr = (std::string *)0; res1 = SWIG_AsPtr_std_string *SWIG_PERL_CALL_ARGS_2(ST(0)*, &ptr); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SchemaValidator_run" "', argument " "1"" of type '" "std::string const &""'"); } if (!ptr) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "SchemaValidator_run" "', argument " "1"" of type '" "std::string const &""'"); } arg1 = ptr; } { std::string *ptr = (std::string *)0; res2 = SWIG_AsPtr_std_string *SWIG_PERL_CALL_ARGS_2(ST(1)*, &ptr); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "SchemaValidator_run" "', argument " "2"" of type '" "std::string const &""'"); } if (!ptr) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "SchemaValidator_run" "', argument " "2"" of type '" "std::string const &""'"); } arg2 = ptr; } { try { result = (bool)libgexf::SchemaValidator::run((std::string const &)*arg1,(std::string const &)*arg2); } catch (const std::exception& e) { SWIG_croak(e.what()); } catch(...) { SWIG_croak("Unknown error"); } } ST(argvi) = SWIG_From_bool SWIG_PERL_CALL_ARGS_1(static_cast< bool >(result)); argvi++ ; if (SWIG_IsNewObj(res1)) delete arg1; if (SWIG_IsNewObj(res2)) delete arg2; XSRETURN(argvi); fail: if (SWIG_IsNewObj(res1)) delete arg1; if (SWIG_IsNewObj(res2)) delete arg2; SWIG_croak_null(); } } ----------------------------------------------------------------------------------------- -------------------------fixed wrapper.cpp---------------------------------------------------- *Fixed code (see items count test and SWIG_PERL_CALL_ARGS_2**):* XS(_wrap_SchemaValidator_run) { { std::string *arg1 = 0 ; std::string *arg2 = 0 ; int res1 = SWIG_OLDOBJ ; int res2 = SWIG_OLDOBJ ; int argvi = 0; bool result; dXSARGS; *if ((items < 3) || (items > 3))* { SWIG_croak("Usage: SchemaValidator_run(xml_file_pathname,xml_schema_file_pathname);"); } { std::string *ptr = (std::string *)0; res1 = SWIG_AsPtr_std_string *SWIG_PERL_CALL_ARGS_2(ST(1)*, &ptr); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SchemaValidator_run" "', argument " "1"" of type '" "std::string const &""'"); } if (!ptr) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "SchemaValidator_run" "', argument " "1"" of type '" "std::string const &""'"); } arg1 = ptr; } { std::string *ptr = (std::string *)0; res2 = SWIG_AsPtr_std_string *SWIG_PERL_CALL_ARGS_2(ST(2)*, &ptr); if (!SWIG_IsOK(res2)) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "SchemaValidator_run" "', argument " "2"" of type '" "std::string const &""'"); } if (!ptr) { SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "SchemaValidator_run" "', argument " "2"" of type '" "std::string const &""'"); } arg2 = ptr; } { try { result = (bool)libgexf::SchemaValidator::run((std::string const &)*arg1,(std::string const &)*arg2); } catch (const std::exception& e) { SWIG_croak(e.what()); } catch(...) { SWIG_croak("Unknown error"); } } ST(argvi) = SWIG_From_bool SWIG_PERL_CALL_ARGS_1(static_cast< bool >(result)); argvi++ ; if (SWIG_IsNewObj(res1)) delete arg1; if (SWIG_IsNewObj(res2)) delete arg2; XSRETURN(argvi); fail: if (SWIG_IsNewObj(res1)) delete arg1; if (SWIG_IsNewObj(res2)) delete arg2; SWIG_croak_null(); } } ----------------------------------------------------------------------------------------- SWIG version : 1.3.40 Thanks for your awesome work ! Seb |
From: Robert S. <ta...@tr...> - 2009-09-16 18:16:25
|
Hi Sébastien, The interpretation SWIG currently takes is to surface this as library function rather than a class method, thus the Perl side call to a function like this looks like "Foo::bar($a, $b)", which does not pass the implicit first argument. The only time I've found this to be a practical limitation is when inheritance is involved, so I've been working out solutions for this on the path to introducing directors to SWIG's Perl support. I have code in a branch ("talby-perl5-improvements") which currently handles declarations like this the way you'd expected, but the code is not yet completely reverse compatible with release versions of SWIG (this call style change is one of the key reasons). So, I'm working on this and I hope that the current release version does not present too much of an issue. If you'd like to give my branch a try and see if the wrappers it generates both work and make sense to you, it would help move that branch closer to release. I suspect it may match your expectations better, but I am still chasing object lifetime issues, so getting wrappers to not leak is sometimes a real trick for now. -Robert On Wed, Sep 16, 2009 at 10:56:43AM +0200, Sébastien Heymann wrote: > Hi, > > It seems that there is a bug while calling a C++ static method with > arguments in Perl binding, e.g.: > |
From: Josh C. <jc...@nc...> - 2009-09-16 18:49:30
|
On Wed, 16 Sep 2009, Robert Stone wrote: > The interpretation SWIG currently takes is to surface this as > library function rather than a class method, thus the Perl side call to > a function like this looks like "Foo::bar($a, $b)", which does not pass > the implicit first argument. > The only time I've found this to be a practical limitation is > when inheritance is involved, so I've been working out solutions for > this on the path to introducing directors to SWIG's Perl support. I > have code in a branch ("talby-perl5-improvements") which currently > handles declarations like this the way you'd expected, but the code is > not yet completely reverse compatible with release versions of SWIG > (this call style change is one of the key reasons). That's great. Thanks for working on this. I have a couple of thoughts. I wonder whether there's an easy way to provide backwards compatibility, so that Foo::bar($a, $b) still works and code that does this won't break. I don't know whether there's a good way to tell whether the first argument is a class or just a string. I've also run into the inheritance problem with enums defined in a class. Perhaps there's a way to handle this too, involving referring to enums with "->" as well. Josh |
From: Robert S. <ta...@tr...> - 2009-09-17 17:25:43
|
Hi Josh, Well, the trick is that SWIG and XS are both structured to be smart about missing arguments on the end of the parameter list. This makes it fairly difficult to be clever about the beginning of the list too. Particularly because in Perl the implicit class reference is really just a simple string containing the name of the class the method was invoked on, so we can't effectively do type matching in many cases. Enums are a different deal because they are currently surfaced as variables, not methods, to Perl. The only way to fake inheritance there would be with explicit copying in your child class. That combined with the fact that Perl is willing to do constant propagation for certain kinds of functions, I've also been working on how to change that. The only kink is that method dispatch is not eligible for that optimization. perl -MO=Deparse -le 'sub foo() { 12 }; print main::foo(); print main->foo()' illustrates that property. There's no way around that problem really, but it does mean that the way you'd want to interact with enums would differ depending on whether inheritance is a factor in your use code. That seems a bit awkward to me, but I may be the only person constant propagation in interpreted languages matters to. -Robert On Wed, Sep 16, 2009 at 02:49:19PM -0400, Josh Cherry wrote: > > I wonder whether there's an easy way to provide backwards compatibility, > so that Foo::bar($a, $b) still works and code that does this won't break. > I don't know whether there's a good way to tell whether the first > argument is a class or just a string. > > I've also run into the inheritance problem with enums defined in a class. > Perhaps there's a way to handle this too, involving referring to enums > with "->" as well. > |