) I am building a module that interfaces to C library that is mostly
) comprised of functions that are supposed to be called like:
)
) status = func(handle, inarg1, inarg2, &outarg1, &outarg2);
)
) The number of arguments vary, but I have up to 6 outargs in one case, a
) mixture of integral and string values. What is the best way to represent
) this in perl? I have so far tried one version (somewhat patterned after
) the POSIX localeconf):
)
) ($status, $hashref) = func($handle, inarg1, inarg2);
)
) Where hashref then contains the indivdiual outargs. But this does not cope
) well if one of the outargs is optional in the C function by specifying
) NULL and is computationally expensive to calculate. Currently all
) arguments are always computed, as my xsub does never specify NULL for any
) outarg. Is there a better way to represent this kind of function in perl?
First, I'd decide the proper XS interface and then decide what
Perl interface you want.
For maximum ease and efficiency, make the XS interface mostly
identical to the C interface:
$status= func( $handle, $inarg1, $inarg2, $outarg1, [], $outarg3 );
where C<[]> means NULL in C. Depending on the data type of the
out args, you'll probably need to make sure each out arg has
enough room for the result (for structs), or is exactly the right
type for the result (for simple data types). In the second case,
I've used this in typemap:
short * T_IVBUF
int * T_IVBUF
long * T_IVBUF
INPUT
T_IVBUF
if( null_arg($arg) )
$var= NULL;
else
*( $var= ($type) _alloca( sizeof(*($var)) ) )= SvIV($arg)
OUTPUT
T_IVBUF
if( ! null_arg($arg) && ! SvREADONLY($arg) )
sv_setiv( $arg, (IV)*($var) );
which won't work if you don't has alloca(). null_arg() is a macro
that goes in your *.xs file and is shown below.
In the first case, I have macros that are okay but should be
better but I'm not sure I can do that without enhancements to
XS itself. What you need for fixed-size structures is:
#ifndef DEBUGGING
# define Debug(list) /*Nothing*/
#else
# define Debug(list) ErrPrintf list
# include <stdarg.h>
static void
ErrPrintf( const char *fmt, ... )
{
va_list alist;
static char *env= NULL;
if( NULL == env ) {
if( NULL == ( env= getenv("DEBUG_XS_ARGS") ) )
env= "";
}
if( '\0' == *env )
return;
va_start( alist, fmt );
vfprintf( stderr, fmt, alist );
va_end( alist );
}
#endif /* DEBUGGING */
/* Is an argument `[]', meaning we should pass `NULL'? */
#define null_arg(a) ( SvROK(a) && SVt_PVAV == SvTYPE(SvRV(a)) \
&& -1 == av_len((AV*)SvRV(a)) )
/* Minimum buffer size to use on first call: */
#define MIN_GROW_SIZE 8
/* Used in Debug() messages to show which macro call is involved: */
#define string(arg) #arg
/* Grow a buffer to a fixed data type: */
#define grow_buf_typ( pBuf,svBuf, Type ) do { \
Debug(( "grow_buf_typ( %s,[%s, %s ); 0x%lX==0x%lX %ld\n", \
string(pBuf), strchr(string(svBuf),'('), string(Type), \
pBuf, SvPVX(svBuf), sizeof(Type) )); \
if( ! null_arg(svBuf) ) { \
if( ! SvOK(svBuf) ) sv_setpvn(svBuf,"",0); \
(void) SvPV_force( svBuf, na ); \
pBuf= (Type *) SvGROW( svBuf, sizeof(Type) ); \
Debug(( "0x%lX==0x%lX %ld of %ld\n", \
pBuf, SvPVX(svBuf), SvCUR(svBuf), SvLEN(svBuf) )); \
} \
} while( FALSE )
For the XS interface shown above, you'd have:
CODE:
grow_buf_typ( outarg1,ST(3), struct one );
grow_buf_typ( outarg2,ST(4), struct two );
grow_buf_typ( outarg3,ST(5), struct three );
RETVAL= func( handle, inarg1, inarg2, outarg1, outarg2, outarg3 );
Now, for the Perl interface. You can just call the XS interface
directly. If you have a lot of out args, then you could do
something like:
func( $handle, $inarg1, $inarg2, {OUTARG1=>$outarg1,OUTARG3=>$outarg3} );
sub func
{
_func( $handle, $inarg1, $inarg2,
$outargs->{OUTARG1} || [],
$outargs->{OUTARG2} || [],
$outargs->{OUTARG3} || [] );
}
That's just one idea. I hope that helps.
--
Tye McQueen Nothing is obvious unless you are overlooking something
http://www.metronet.com/~tye/ (scripts, links, nothing fancy)