Let's say the user has two (or more) functions that are overloaded (C++ obviously), and that it's not trivial to implement them as a single variable-parameter function. Obviously XS can't deal with overloaded functions -- it's just too much a C++ thing. But there is another hope, and one that's often used when using Inline::C or Inline::CPP to interface with external libraries: Write a wrapper function.
Another limitation of Inline::CPP is that you can't expose templates directly to Perl. As with function overloading, Perl XS would have no idea what to do with template definitions. And C++ wouldn't know how Perl intends to call the functions, and thus wouldn't be able to expand the templates at compile-time. But once again, simple wrappers can easily hide templates from Perl/XS.
Ok, we're not dealing with templates here... just overloading. Either way the following will work. Write a wrapper that accepts a variable argument list, and then dispatches a call to the overloaded functions based on how many args are provided. It's a little cumbersome once you get more than a handful of variations to deal with, but I don't think people generally create more than a handful of overloads anyway (they just turn to templates, which is why I mentioned templates earlier).
So here's a hybrid: The overloaded functions (which won't bind to Perl), and the wrapper (which will):
use strict;
use warnings;
use v5.16;
use Inline CPP => 'DATA';
say multiadd( 1 ); # No dispatch; just return the value.
say multiadd( 1, 2 ); # Dispatch add( int, int ).
say multiadd( 1, 2, 3 ); # Dispatch add( int, int, int ).
say multiadd( 1, 2, 3, 4 ); # No dispatch; throw an exception.
__DATA__
__CPP__
#include <stdexcept>
// Overloaded functions: add(int,int), add(int,int,int): No (reliable*
+) Perl binding.
int add ( int a, int b ) {
return a + b;
}
int add ( int a, int b, int c ) {
return a + b + c;
}
// XS binding will be generated for multiadd().
int multiadd ( SV * a, ... ) {
dXSARGS;
try{
switch ( items ) {
case 1: return SvIV(ST(0));
case 2: return add( SvIV(ST(0)), SvIV(ST(1)) );
case 3: return add( SvIV(ST(0)), SvIV(ST(1)), SvIV(ST(2)) );
default: throw std::runtime_error(
"multiadd() - Too many args in function call"
);
}
}
catch ( std::runtime_error msg ) {
croak( msg.what() ); // Perl wants exceptions via croak.
}
}
This same approach could be used to dispatch calls based on any function signature criteria; number of parameters, or data-types. Additionally, a similar approach works for dispatching to template-generated functions or classes.
*(reliable): I think the last overload defined will bind via XS, but the others will be masked. It's not worth even trying to call directly.
|