XSUB detecting if it was called via Package->method() or Package::method()?

C

Chris

So, if I call
Package->method(),
then the string "Package" will be the first item in @_ when inside the
method() sub.

If I call
$object->method(),
then $ will be the first item in @_ when inside the method() sub.

If I call Package::method(), @_ will be empty.

perldoc perlobj calls the first one a "class method", because it
expects the class name as the first argument, and the second one an
"instance method", because it expects an object reference as the first
argument. The third one I consider a "static method", because it
doesn't expect the first argument to be anything special.

How can an XSUB differentiate between those 3?
 
U

Uri Guttman

C> So, if I call
Package-> method(),
C> then the string "Package" will be the first item in @_ when inside the
C> method() sub.

C> If I call
C> $object->method(),
C> then $ will be the first item in @_ when inside the method() sub.

C> If I call Package::method(), @_ will be empty.

C> perldoc perlobj calls the first one a "class method", because it
C> expects the class name as the first argument, and the second one an
C> "instance method", because it expects an object reference as the first
C> argument. The third one I consider a "static method", because it
C> doesn't expect the first argument to be anything special.

the third one isn't a method at all. methods are ONLY called with the ->
notation (or by indirect calls which you should avoid). the :: style is
just a fully qualified sub name being called. no different than if you
imported that sub or declared it locally. methods will use inheritence
and the method name can be dynamically created. you can't do that with
sub names without disabling strict. those are some major differences.

C> How can an XSUB differentiate between those 3?

check the first arg type should be easy enough but as with plain perl
you should NEVER allow multiple call styles for a single sub. instance
methods should only be called by objects, class methods via a package
name and plain subs with neither of those. so your problem is solved,
you don't need to know. it is up to the caller to make sure they do what
you document.

uri
 
C

Chris

check the first arg type should be easy enough but as with plain perl
you should NEVER allow multiple call styles for a single sub.

What if the sub is AUTOLOAD? If I have defined a sub called
Package::AUTOLOAD, then Package->method('foo'), $package-
method('foo'), and Package::method('foo') will both redirect to the
AUTOLOAD sub. AUTOLOAD then doesn't know if $_[0] is $self, the
string 'Package', or the string 'foo'.
 
U

Uri Guttman

C> What if the sub is AUTOLOAD? If I have defined a sub called
C> Package::AUTOLOAD, then Package->method('foo'), $package-C> AUTOLOAD sub. AUTOLOAD then doesn't know if $_[0] is $self, the
C> string 'Package', or the string 'foo'.

but it knows the NAME of the sub. so it can dispatch based on that. a
sub of a given name should only have one api style. it doesn't matter
how the sub actually gets invoked but how you pass it args.

uri
 
S

sisyphus

How can an XSUB differentiate between those 3?

########################################
use warnings;
use Math::BigInt;

$x = Math::BigInt->new(100123);


Math::BigInt->foo(42); # class
$x->foo(42); # instance
Math::BigInt::foo(42); # static

package Math::BigInt;

use Inline C => Config =>
BUILD_NOISY=>1;

use Inline C => <<'EOC';

void foo(SV* x, ...) {
dXSARGS;
if(items == 1) printf("static method\n");
else {
if(SvPOK(ST(0))) {
if(strEQ(SvPV_nolen(ST(0)), "Math::BigInt")) printf("class
method\n");
else printf("wtf?\n");
}
else printf("instance method\n");
}
XSRETURN(0);
}

EOC
#########################################

For me, after compiling, it prints:

class method
instance method
static method

Not entirely sure that the logic caters for all possibilities, but you
should get the idea.

When using ellipsis syntax with Inline::C you have to provide at least
one argument - ie foo() must take at least one argument, and that's
why I've provided a (fudge) argument of 42 ... and also why we have to
check for items==1 (not items==0).

In XS, I don't think that stipulation of "at least one arg" applies
(though I don't really know for sure).

Cheers,
Rob
 
U

Uri Guttman

s> Not entirely sure that the logic caters for all possibilities, but you
s> should get the idea.

and if you read my reply, you will see that this is a big mistake. one
sub should not be called with different apis. the whole question is
wrong.

uri
 
C

Chris

Let me put this in a little more context.

I'm writing Perl bindings to Qt. So, there's a lot of methods,
including method overloading. So, for instance, in C++ you can write
QApplication app(argc, argv);
QObject::connect( &app, SIGNAL(aboutToQuit()), &app,
SLOT(quit()) );

or you could write
QApplication app(argc, argv);
app.connect( &app, SIGNAL(aboutToQuit()), &app, SLOT(quit()) );

Both forms are valid, and I'd like for both forms to be valid in Perl,
too.

In my Perl bindings, every method call goes through 1 AUTOLOAD sub.
From the information in the AUTOLOAD sub, I have to determine the
correct C++ method to call. The only reliable information is the
$AUTOLOAD variable, which tells me 1) the name of the method being
called and 2) the package that method is in. The other information I
need to know is how many arguments were passed. Unfortunately,
because $_[0] (or, since I'm in XS, ST(0)), may be the object doing
the call, I can't rely on the "items" variable. It might be 1 more
than the number of arguments passed, it might not.



Let's say I have the following C++ methods:
static QObject::doSomethingWith( QObject*, QObject* );
QObject::doSomethingWith( QObject* );

to call those methods in Perl I'd write:
QObject::doSomethingWith( $object1, $object2 );
$object1->doSomethingWith( $object2 );

Then in AUTOLOAD I get the same information for both methods:
$AUTOLOAD = 'QObject::doSomethingWith'
@_ = ( $object1, $object2 )

If I could differentiate between the instance method ("$object1-
doSomethingWith") and the "static" method
("QObject::doSomethingWith"), then I could determine the correct C++
method to call.

I do have a workaround for this, but it's pretty hack-tastic, and I'm
trying to see if there's a cleaner solution. But it's looking like
Perl doesn't supply me with the information I'm looking for.
 
S

sisyphus

  s> Not entirely sure that the logic caters for all possibilities, butyou
  s> should get the idea.

and if you read my reply, you will see that this is a big mistake. one
sub should not be called with different apis. the whole question is
wrong.

Yes, I've read your reply.

Cheers,
Rob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top