How to find current package name from perl XS?
Asked Answered
H

1

9

To get current context I find caller_cx function in perlapi. But there is no description for structure. In perl source code perl.h I can find only this typedef:

typedef struct context PERL_CONTEXT;

Is there examples how to use this struct returned by caller_cx to find current package from XS?

Heligoland answered 1/3, 2017 at 12:18 Comment(1)
The struct context is defined in cop.h. And caller_cx is a macro that calls Perl_caller_cx (defined in pp_ctl.c). (not really an answer, just some help)Chiliad
D
6

The context struct is defined cop.h as mentioned by @Dada in the comments:

struct context {
    union {
    struct block    cx_blk;
    struct subst    cx_subst;
    } cx_u;
};

also the block structures are defined in cop.h.

By inspecting the C implementation of the Perl caller function in pp_ctl.c (line 1850), I think you can get the package name using the following code:

const PERL_CONTEXT *cx = caller_cx(0, NULL);
char *pack_name = HvNAME((HV*)CopSTASH(cx->blk_oldcop));
Doggish answered 1/3, 2017 at 14:29 Comment(12)
By inspecting that I see that there HvNAME_HEK is used. What is the difference in compare to HvNAME?Heligoland
I am not sure. HvNAME_HEK() is defined at line 298 in hv.h. The comment on the line above says "This macro may go away without notice.". HvNAME() is defined on line 280, and it just calls HvNAME_get() on line 300.Widera
Also HvNAME_HEK() returns a HEK *, whereas HvNAME() returns a char *, see also perlapiWidera
Strange, but this code always print: (nil)<<<. CODE: const PERL_CONTEXT *cx = caller_cx(0, NULL); printf( "%p<<<\n", cx );Heligoland
I did a quick test of the method here. It seems works as expected (it prints the caller's package name). You can try clone the repository and run the test script p.pl yourself if you like. I am using Ubuntu 16.10 and Perl v.5.24.Widera
Yeah, your example works fine. But I call caller_cx from XS module. And it does not work. REPO. You also may clone it and perl Makefile.PL && make && cd bin && ./xs.plHeligoland
Yes your example gives me also a null pointer for cx. It is very strange, I have looked at the .c file produced by Inline::C and the one obtained by make from perl Makefile.PL and they look the same as far as I can see. However, I was not able to figure out how Inline::C loads the extension module MyTest.so, and what it puts instead of lib/MyTest.pm.. Maybe there are some clues hidden there?Widera
I think I found something. The xsub MyTest::get_package() is called from the top level main:: namespace. When calling caller() or caller_cx() from the top level main package they will return undefined ( or NULL). Here is a new test that actually works. To run it: cd lib/My; perl Makefile.PL; make; cd ../..; p.plWidera
Maybe that because of caller: In scalar context, returns the caller's package name if there is a caller (that is, if we're in a subroutine or eval or require) and the undefined value otherwise. caller never returns XS subs and they are skipped. Compare: perl -e 'print +(caller)[0]' (like test. XS has no frames) and perl -e 'sub{ print +(caller)[0] }->()' (like test2. sub call has frames).Heligoland
But anyway: if we're in a subroutine or eval or require the main script is eval'ed, is not? so we should always be in eval and print +(caller)[0] should print 'main'Heligoland
@EugenKonkov Hi Eugen. See update to ticket by Dave Mitchell rt.perl.org/Public/Bug/Display.html?id=130903. Does that work for you?Widera
Thank you for tracking. That is same suggestion as mine: perl -e 'print sub{ +(caller)[0] }->()'Heligoland

© 2022 - 2024 — McMap. All rights reserved.