Perl newXS() with closure added
Asked Answered
H

3

7

I want to embed Perl in a c++ application and am looking for a method to call into c++ from perl via newXS(). Apart from the function pointer I need the associate a custom pointer to the CV created by newXS(). The pointer contains a C++ context. I dont want to use globals for this. Is there a common way to do this?

On a wider scope the question is maybe weather there is the possibility to add a closure to the CV created by newXS() and how to reference it when the c function is called that was registered with it. CvPADLIST() would seem the perfect place, however for XSubs it seems to be invalid to use when PERL_IMPLICIT_CONTEXT is set (comment in the beginning of perl's pad.c. Can it maybe be ignored?). Is there some other place I can put CV local data?

Hetman answered 26/8, 2017 at 14:24 Comment(2)
I don't know about XS but you could always have the XS function take an explicit argument and do the closure wrapping in pure Perl: sub make_closure { my $ctx = new_context(); return sub { xs_func($ctx, @_) }; }Bedwarmer
@Bedwarmer Thats interesting, I could for each xs function registered dynamically generate a wrapper via eval... That is one possibility. Was looking at PERL_MAGIC_ext, maybe another way...Hetman
H
3

There's an ANY slot in CV that can be used for custom data and accessed with CvXSUBANY(cv). For example:

CvXSUBANY(cv).any_ptr = my_ptr;

This slot is normally used to store the index for XS ALIASes and the function pointer for XS INTERFACEs.

Hedvig answered 27/8, 2017 at 13:14 Comment(0)
H
5

One possibility is the attach a PERL_MAGIC_ext magic to the SV as described in perlguts:

int  m_free (pTHX_ SV *sv, MAGIC* mg){ ... }
STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, m_free, 0, 0, 0 };
struct ctx;

XS(XS_some_func)
{
    ...
    MAGIC *mg;
    if ((mg = mg_findext((SV*)cv, PERL_MAGIC_ext, &my_vtbl))) {
        ctx *priv = (ctx *)mg->mg_ptr;
    }
    ...
}

And assigning the magic when creating CV via newXS():

   ctx c;
   ...
   CV *cv = newXS(index, XS_some_func, __FILE__);
   MAGIC *mg = sv_magicext((SV *)cv,
                            0,
                            PERL_MAGIC_ext,
                            &my_vtbl,
                            (const char*)&c,
                            sizeof(c));
Hetman answered 26/8, 2017 at 16:28 Comment(0)
H
3

There's an ANY slot in CV that can be used for custom data and accessed with CvXSUBANY(cv). For example:

CvXSUBANY(cv).any_ptr = my_ptr;

This slot is normally used to store the index for XS ALIASes and the function pointer for XS INTERFACEs.

Hedvig answered 27/8, 2017 at 13:14 Comment(0)
C
2

The simplest (and most likely best) approach would probably be to make the context explicit – expose an object oriented API and use methods instead of functions. When in Perl code a new instance of the class is created you put the context into that object. When your XSUB is called as a method on that object, it will receive the context as first parameter (i.e. ST(0)).

That is basically equivalent to melpomene's comment from the XS/C++ perspective, but does not require the extra wrapper closure.

If only one context per process exists, using global variables would also be legitimate – perhaps a necessary evil. Compare also Safely storing static data in XS.

I know of no mechanism to directly associate extra data with xsubs. It may be possible to pull of some magic with the CV, but that sounds unnecessarily complicated unless you can't afford to put your context into a Perl object.

Charnel answered 26/8, 2017 at 15:27 Comment(4)
The approach of an explicit context feels natural if you write an extension and call c/c++ code from the wrapping main perl interpreter. However when using perl as an embedded interpreter with C++ as the wrapping language, then the C++ functions called from embedded perl should reside in the context of the wrapping C++ program. Otherwise it gets for instance tedious to embed multiple interpreters in a local function at the same time.Hetman
@KonradEisele Hmm, I see your point. In that case it may make sense to load a single context object into a Perl package variable when you initialize the interpreter?Charnel
ok, that would be possible. However that would only solve one particular problem. I'm playing around with github.com/tomaka/luawrapper and want a mechanism to implement something similar. I now found magic of type PERL_MAGIC_ext that I can assign, so I guess I will try with this.Hetman
Oh wow, that luawrapper looks incredibly convenient :) Using magic is legitimate, though I'd personally still prefer the more obvious object-based solutions. Will you type up your solution as an answer?Charnel

© 2022 - 2024 — McMap. All rights reserved.