Math::Random::MT's rand is actually a Perl function.
srand is an XS function, so it expects to be called from Perl.
You'll need to use one of the methods in perlcall in both cases.
If you wanted to bypass Perl, you'd have to convert MT.pm and MT.xs to C, then compile and link MT.c, the converted code and your version of List/Util.c together.
| [reply] [d/l] [select] |
I'm unsure about how to call a routine located in another XS module from within an XS module
One solution - if, in Foo.xs, we want to access a function (let's call it 'bar') from Bar.xs, then we have the Bar module make available a pointer to the bar function. The Foo.xs functions then access the bar function via that pointer.
I have a full but minimal working demo of this principle on this machine. I could put copies of the files (Foo.pm, Bar.pm, Foo.xs, Bar.xs, Makefile.PL's and test.pl's) on my scratchpad if that's going to serve any purpose. Note that, with this approach, you'll probably be making significant alterations to both source distros. I think it's the best approach to calling xsubs in one extension from xsubs in another extension .... but I'm not so sure that this is the best way for you to achieve your ends in this particular case.
For a basic demo of passing functions by pointer, here's an Inline::C script that creamygoodness posted a while back:
use strict;
use warnings;
use Inline C => Config =>
BUILD_NOISY => 1;
use Inline C => <<'END_C';
typedef void
(*hello_func_t)(SV * x);
typedef SV *
(*sv_func_t)(SV * x);
void
hola_mundo(SV * x)
{
printf("Hola, mundo! %d\n", (int)SvIV(x));
}
SV *
hola_mundo2(SV *x) {
printf("Hola, mundo2! %d\n", (int)SvIV(x));
return newSViv((int)SvIV(x) * 2);
}
SV*
get_hola_mundo_func_ptr()
{
return newSViv( PTR2IV(hola_mundo) );
}
SV*
get_hola_mundo2_func_ptr()
{
return newSViv( PTR2IV(hola_mundo2) );
}
void
do_hello(SV *sv_with_func_ptr, SV * x)
{
IV temp = SvIV(sv_with_func_ptr);
hello_func_t hello = INT2PTR(hello_func_t, temp);
hello(x);
}
SV *
do_hello2(SV *sv_with_func_ptr, SV * x)
{
IV temp = SvIV(sv_with_func_ptr);
sv_func_t hello = INT2PTR(sv_func_t, temp);
return hello(x);
}
END_C
my $num = 123458;
my $func_ptr = get_hola_mundo_func_ptr();
do_hello($func_ptr, $num);
my $func_ptr2 = get_hola_mundo2_func_ptr();
my $ret = do_hello2($func_ptr2, $num);
print $ret;
Cheers, Rob | [reply] [d/l] |
Note that, with this approach, you'll probably be making significant alterations to both source distros.
That's the problem, it shouldn't not be necessary to c&p code from both modules, into a third, to do this. It makes a mockery of having libraries. And if I link the MT code directly from the List::Util code, I'm effectively including the MT code directly into the List::Util package.
As the anonymonk suggests, it is perfectly possible to call the Perl front end for an XS module in another package using call_pv( "Math::Random::MT::rand" );. But that means going through both the Perl and XS wrappers to get from C to C.
Introducing both overhead (which is what we're trying to avoid by using XS), and complexity: wrapping C arguments into Perl values (SVs) in order to pass them into the routine we're calling and then unwrapping the Perl responses back to C values. On top of that you need to add an extra Perl level block scope to protect your layer from the possibilities of the routine you're calling messing with the Perl stack. And if you get the stack handling wrong, it's a nightmare to debug.
The routines I need to call in MT.xs are (become) straight C functions that live in MT.dll:
Math::Random::MT
mt_setup(seed)
U32 seed
CODE:
RETVAL = mt_setup(seed);
OUTPUT:
RETVAL
double
mt_genrand(self)
Math::Random::MT self
CODE:
RETVAL = mt_genrand(self);
OUTPUT:
RETVAL
In normal C code, calling a routine from within a DLL is just a case of calling LoadModule() followed by GetProcAddress() (or the platform equivalents) and you're good to go. But XS dlls do not export the addresses of the routines they contain directly. Instead, they export a boot_MODULENAME entrypoint and Dynaloader::bootstrap takes care of calling that entrypoint to obtain the list of "exported" subroutines and installs them (in the current package? Current to where? The calling perl code or the calling XS code?).
But understanding that process and replicating it from within XS code is another nightmare.
The right way to do what I want to do, would be to:
- Determine at loadtime (of List::Util) whether Math::Random::MT is available.
- If so, load it and obtain the addresses of the two C routines above.
- And call them in place of seedDrand01() and Drand01() within List::Util::shuffle()
In theory, those are 3 fairly simple steps. In practice, I haven't a clue how to go about the first two. Or where to go to get assistance. As in the past, XS questions at this level get precious little if any responses (except from your own good self) :(
There is already a module out there that provides a MT-based shuffle: Math::Random::MT::Auto. It goes the route of incorporating it's own implementation of the Mersenne Twister PRNG. However, it comes with a bunch of other stuff I have no need or desire for and is much slower than List::Util and Math::Random::MT. I also have some doubts about the implementation of the MT that I don't have with the M::R::MT version. And it's a fundamentally bad thing to have two independant PRNGs in use at the same time.
As always when I start trying to mess with XS. I know what I want to do, I just lack the knowledge of how to achieve it, or how to get expert assistance in trying to achieve it. What we need is an XSMonks site, but if we threw the party, do you think anyone would turn up?
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
it shouldn't not be necessary to c&p code from both modules, into a third
It's not (at least in the scenario that I was referring to) ... you have to add some xsubs to "both" modules, but there's no need for a third module.
But XS dlls do not export the addresses of the routines they contain directly
Having never tried to directly access a perl dll's functions before, I hadn't really considered that ... but you're quite right. And that leads me to wonder just how one would, eg, access a perl dll's functionality in an Inline::C or XS situation on Win32. Any takers ? Presumably, GetProcAddress() is useless to us as it returns the address of *exported* functions/variables only. But, equally presumably, it *is* possible to get at the dll's functionality ...
Cheers, Rob
| [reply] |
| [reply] |