in reply to Re^2: Pursuading an XS module to use a routine from another XS module.
in thread Pursuading an XS module to use a routine from another XS module.

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
  • Comment on Re^3: Pursuading an XS module to use a routine from another XS module.
  • Download Code

Replies are listed 'Best First'.
Re^4: Pursuading an XS module to use a routine from another XS module.
by BrowserUk (Patriarch) on Sep 03, 2008 at 07:36 UTC
    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:

    1. Determine at loadtime (of List::Util) whether Math::Random::MT is available.
    2. If so, load it and obtain the addresses of the two C routines above.
    3. 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.
      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
        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.

        Um. If one ever hopes that others would be able to replicate ones results, then you either create a third module from the two and upload that to CPAN, or you draft patches for both modules and try to get them accepted by both authors. The former is easier and more likely to succeed, but wholly unsatisfactory.

        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 ?

        I've been trying to pick my way through dynaloader to work out what/how/when it does what it does, but it was written/influenced by that bunch of core people back in the mid 90's that don't (have never) dained to share their knowledge and expertise with us plebeians here at PM :(

        Even the few of that illustrious list that have ever lowered themeselves to posting here at PM, seem to have disconnected from this part of the community.


        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.
Re^4: Pursuading an XS module to use a routine from another XS module.
by Anonymous Monk on Sep 03, 2008 at 03:15 UTC
    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.

    Why? Its so much easier to just call things by name, thats why we have namespaces.