sunshine4 has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to make Perl bindings for two C++ objects. In C++, their usage is:

SecurityCredentials *credentials = new SecurityCredentials(service_url +, some_secret_key); credentials->IsValid(); credentials->ChangeKey(new_secret_key); // Does occur during a normal +program as these change frequently. AlarmZone zone(credentials, 0); zone->IsArmed(); zone->SetArmed(true); AlarmZone another_zone(credentials, 1); another_zone->IsArmed(); another_zone->SetArmed(true);
As you can see, a SecurityCredentials object exists in isolation. An AlarmZone object however requires a pointer to a SecurityCredentials throughout its lifetime. Following perlxs (using ExtUtils::MakeMaker, h2xs type tools), I got the SecurityCredentials object working fine; the XS file looks like:
SecurityCredentials* SecurityCredentials::new(const char* service_url, const char* service_ +credentials) CODE: RETVAL = new SecurityCredentials(service_url, service_credentials); OUTPUT: RETVAL void SecurityCredentials::DESTROY() bool SecurityCredentials::IsValid() ...

Now comes the time to implement AlarmZone. I am hoping the usage looks like:

my $security_credentials = SecurityCredentials->new(service_url, some_ +secret_key); my $zone = AlarmZone->new($security_credentials, 1); $zone->IsArmed(); # Some time later

The reference counting of $security_credentials is my problem: For the C++ AlarmZone to work, its pointer to the C++ SecurityCredentials must be valid. However since things are running from Perl land, $security_credentials may go out of scope and be destroyed, prematurely cleaning up the underlying C++ object required by AlarmZones.

For example, in the following example AlarmZone should still work, even though $security_credentials goes out of scope:

sub SillySub { my $security_credentials = SecurityCredentials->new(service_url, som +e_secret_key); return AlarmZone->new($security_credentials, 1); }

My ideas to fix this so far have been:

Any suggestions?

Thanks! :)

Replies are listed 'Best First'.
Re: Using multiple XS objects together
by salva (Canon) on May 09, 2013 at 08:38 UTC
    I am stuck working out the details though, in particular where to store the security_credentials pointer (so it can be used later in DESTROY()).

    You can change AlarmZone typemap to wrap it with an AV (a perl array) then use the first slot for the AlarmZone pointer and the second for the SecutiryCredentials wrapper.

    Another option is to extend AlarmZone and SecurityCredentials classes adding a pointer to the Perl wrappers so you can navigate in both directions.

    update: Maybe a simpler solution would be to use a fieldhash.

Re: Using multiple XS objects together
by Anonymous Monk on May 09, 2013 at 08:29 UTC

    I think you can solve your issue by using ExtUtils::XSpp - XS for C++ and having a member of AlarmZone be a pointer to SecurityCredentials

    Wx makes much use of XSpp

Re: Using multiple XS objects together
by bulk88 (Priest) on May 10, 2013 at 21:38 UTC
    The XS part of AlarmZone::new(SV* security_credentials, int zone) increases the reference count of security_credentials with SvREFCNT_inc() or similar, retains a pointer to the SV*, and decreases the reference count in AlarmZone->DESTROY(). This is the cleanest idea I can think of (reference counting stays in the Perl area of the program, C++ part continues unaware of Perl's existence.). I am stuck working out the details though, in particular where to store the security_credentials pointer (so it can be used later in DESTROY()).
    You have to add magic to the inner blessed SV with sv_magicext, here is an example.
    /* declare as 5 member, not normal 8 to save image space*/ const static struct { int (*svt_get)(SV* sv, MAGIC* mg); int (*svt_set)(SV* sv, MAGIC* mg); U32 (*svt_len)(SV* sv, MAGIC* mg); int (*svt_clear)(SV* sv, MAGIC* mg); int (*svt_free)(SV* sv, MAGIC* mg); } my_vtbl = { NULL, NULL, NULL, NULL, NULL }; /* puts newsv, refcnt++ed (caller doesn't have to do it), in sv as hid +den magic SV */ STATIC void setMgSV(pTHX_ SV * sv, SV * newsv) { MAGIC * mg; if(SvRMAGICAL(sv)) { /* implies SvTYPE >= SVt_PVMG */ mg = mg_findext(sv, PERL_MAGIC_ext, &my_vtbl); if(mg) { SV * oldsv; SvREFCNT_inc_simple_void_NN(newsv); oldsv = mg->mg_obj; mg->mg_obj = newsv; SvREFCNT_dec(oldsv); } else { goto addmg; } } else { addmg: sv_magicext(sv,newsv,PERL_MAGIC_ext,&my_vtbl,NULL,0); } } # xsub to attach a hidden SV in RV inside to the target SV of RV outsi +de # both params must be references # void SetMagicSV(outside, inside) void SetMagicSV(...) PREINIT: SV * outside; SV * inside; CODE: if(items != 2) croak_xs_usage(cv, "outside, inside"); inside = POPs; outside = POPs; PUTBACK; if(SvROK(outside) && SvROK(inside)) { outside = SvRV(outside); inside = SvRV(inside); } else{ croak_xs_usage(cv, "outside, inside"); } setMgSV(aTHX_ outside, inside); return;
    The other ideas is to have the inner SV be an AV or HV instead of an SVIV as is typically for XSPP wrapped C++ objs.

    Another idea is link the 2 perl objs that mirror C++ objs in pure perl in a public API pure perl obj that has refs to the 2 XS objs to make sure 1 of the XS objs doesn't go out of scope. I've seen in Perl's TAP/Test modules AUTOLOAD accessors that forward the outer obj's meth call to the inner obj's method from an AUTOLOAD-ed outer obj's method that doesn't exist in code, since you can't @ISA the inner obj class into the outer obj since the 2 objs are differently layed out internally (the outer is an hash, the inner a SVIV C++ obj). Someone else can describe more about that idea.
      *ick* too low-level, use the glue, the glue :)