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

Hello,

I have a perl object, which is a blessed reference to a hash visible to Perl. That visible hash is tied to a hidden hash via PERL_MAGIC_ext (to keep the Perl script's grubby fingers off my precious data). I have the following problem code:

void must_not_delete_cpp_object(SV* perl_obj, bool must_not_delete) { HV* hidden_hash; IV can_delete; SV** can_delete_sv; hidden_hash = get_hidden_hash(perl_obj); can_delete_sv = hv_fetch(hidden_hash, "_can_delete", 11, 0); can_delete = must_not_delete ? 0 : 1; sv_setiv(*can_delete_sv, can_delete); hv_store(hidden_hash, "_can_delete", 11, *can_delete_sv, 0); }

There would seem to be no way for the hash value to be anything but 1 or 0, right? And normally it is indeed one of those two values. But when called from a C++ function instead of from XS, it's the address of the visible hash (for example, 403291944), which isn't even used in this function; it's used in get_hidden_hash().

If I put this warning: warn("Setting can_delete to %d in %d", can_delete, (IV)hidden_hash); right before the sv_setiv line, I get the following as it dies:

Setting can_delete to 0 in 403291976 at test.pl line 136. Can't coerce HASH to integer in subroutine entry at test.pl line 136.

If I put the same warning right after, I get the Can't coerce... line and it dies

So somehow, when my code is called from a C++ function, the line can_delete = must_not_delete ? 0 : 1 ends up with a hash address but can_delete is still typed as an integer. With the warning added, the value is correct, but the type somehow gets changed to a HASH and the sv_setiv line causes death. This happens even if the warning hasn't been emitted, so the mere presence of the warning causes a change in type.

But this only happens when called from C++; when called from XS, can_delete is an integer and is either 0 or 1, the warning is emitted, the hash element is set, and the script goes merrily on its way.

The background is, I am creating some subclasses and linking them to Perl classes. Most of the time, I want the C++ object to be deleted when the Perl object goes out of scope. But sometimes the system takes ownership of the objects, and I can't delete them.

If I already know when I create the object that the system is going to take ownership, I call the function from my XS new() function:

must_not_delete_cpp_object(RETVAL->perl_obj, true);

In other cases, the system may or may not take ownership, depending on what the Perl script does with the object; in these cases, the C++ object may be deleted by the system while the Perl object is still in scope, so I call the function from the C++ destructor:

must_not_delete_cpp_object(perl_obj, true);

I see no difference between these two calls, other than the fact that one uses an member variable from within a class and the other from without. And yet the second call gives the odd results shown above.

For reference, here's get_hidden_hash():

HV* get_hidden_hash(SV* perl_obj) { SV* underlying_hash; MAGIC* mg; HV* hidden_hash; // get the underlying hash that the perl_obj is a reference to // (we can leave it an SV* because we're just using it to find mag +ic) underlying_hash = SvRV(perl_obj); // get the hidden hash linked to the perl_obj mg = mg_find(underlying_hash, PERL_MAGIC_ext); hidden_hash = (HV*)mg->mg_obj; return hidden_hash; }

Replies are listed 'Best First'.
Re: Integer seen as hash?
by ikegami (Patriarch) on Jun 07, 2011 at 16:37 UTC

    The problem can't possibly be with can_delete. An int is an int. No bit pattern is invalid.

    If there's a problem on that line, it must be *can_delete_sv, or in magic attached to it.

    That's as much as I'm tempted to work on this without a minimal runnable demo. I would expect the 4th param of hv_fetch to be true, by the way, and that probably removes the need to call hv_store.