Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Re^2: Inline::C and NULL pointers

by markong (Pilgrim)
on Dec 19, 2021 at 12:02 UTC ( [id://11139728]=note: print w/replies, xml ) Need Help??


in reply to Re: Inline::C and NULL pointers
in thread Inline::C and NULL pointers

Hi and thanks for both the replies!

I should have probably been more explicit about this: the function you're looking at (enctypex_msname) is just an "utility" extracted from a big file which encode/decode data, which has been written by a third party.
The C is quite a mess (both in terms of structuring and often uses contrived logic) and the author uses those  if (name_of_pointer_char) {} tests to see if something was passed into the function. I have no control over the code, or rather I'd like to stay away from it as much as possible :).
But I need to decode some data and make some tests and doing it from Perl will be a lot faster: I won't rewrite the code to be "XS compatible" because that doesn't make sense (I would rather try to rewrite the thing directly in Perl) but I need to pass "NULL" pointers to the functions and unless there's some quick recipe for doing that with Inline::C, I'd better write a swig interface for the library and pass undef as needed.

Replies are listed 'Best First'.
Re^3: Inline::C and NULL pointers
by syphilis (Archbishop) on Dec 19, 2021 at 21:42 UTC
    but I need to pass "NULL" pointers to the functions

    How to do that is an interesting puzzle. I've been playing with this little Inline::C demo:
    use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1, ; use Inline C =><<'EOC'; unsigned char * foo(unsigned char * name) { if(name) printf("True\n"); else printf("False\n"); return(NULL); } unsigned char * bar() { return (foo(NULL)); } EOC foo(undef); bar(); __END__ Outputs: True False
    The question is: "How can I call foo() directly from perl such that it will output "False" ? One way is to call bar() which then calls foo() - and I think that the OP's existing enctypex_msname function could be accessed (as is) via this technique of calling a wrapper function . But that's still not a direct call to foo().

    AFAICT, the issue is the typemapping of "unsigned char *" (which can be found in perl's lib/ExtUtils/typemap file).
    This default typemapping types "unsigned char *" to T_PV, but if we change that to T_PTR, then calling foo(undef) in my little demo will have it output "False" as desired. (I expect this is what SWIG, in effect, does.)

    Inline::C allows us to use our own typemaps, so I wrote an alternative typemap for unsigned char * and I placed that file (named "nullmap.txt") in the same directory as the script, and pointed the script to it (in the inline configuration section). Unfortunately, the default typemapping was still used.
    So ... as proof of concept, I replaced "unsigned char *" in nullmap.txt with "unmapped". It looks like this:
    # plagiarised from default perl typemap unmapped T_PTR INPUT T_PTR $var = INT2PTR($type,SvIV($arg)) OUTPUT T_PTR sv_setiv($arg, PTR2IV($var));
    Then, in the script, I typedeffed the "unsigned char *" to "unmapped", and replaced "unsigned char *" with "unmapped" in the function declaration:
    use strict; use warnings; use Inline C => Config => TYPEMAPS => "nullmap.txt", BUILD_NOISY => 1, ; use Inline C =><<'EOC'; typedef unsigned char * unmapped; unmapped foo(unmapped name) { if(name) printf("True\n"); else printf("False\n"); return(NULL); } unsigned char * bar() { return (foo(NULL)); } EOC foo(undef); bar(); __END__ Outputs: False False
    So we have 2 essentially identical scripts that output different results. One of them specifies an "unmapped" type where the other specifies "unsigned char *" - yet the two types are the same thing. The difference occurs because the two types employ different typemapping.

    Note that it should not be necessary to replace *all* occurrences of "unsigned char *" with "unmapped" (or whatever replacement name is chosen) - just renaming those occurrences found in the declaration would be all that's needed.
    And then there's the question of whether both the return type and the argument type should be rewritten to "unmapped". (Perhaps it's only the argument type that needs to be renamed.)
    Also, when fiddling around with Inline scripts, we need to remember that a change to the Inline Config section will not trigger a recompilation of the C code unless that Config section specifies FORCE_BUILD => 1. Otherwise, it's necessary to alter the actual code section.

    I'm surprised that specifying an alternative Inline::C typemap doesn't override the default typemap for any types that are specified in both.
    Is that an Inline::C bug ? Or a design flaw ?
    Maybe it's the way it has to be.

    Cheers,
    Rob

      I think I see how typemaps work (bug, skip to end)

      Digging down T_PTR is clobbered even if you don't specify the default typemap (see output of program, BROKEN_ gets replaced )

      #!/usr/bin/perl -- use strict; use warnings; use ExtUtils::ParseXS::Utilities qw/ process_typemaps /; use Data::Dump qw/ dd /; dd( process_typemaps( './mytypemap.txt', ) ); __END__

      So it makes sense or its documentation bug or behavior bug ... I dunno , I haven't used a keyboard in 6 months

        Indicates that a user-supplied typemap should take precedence over the default typemaps. This option may be used multiple times, with the last typemap having the highest precedence.

        Thanks for digging that up.
        I really do think it would be better if our user-supplied typemap took precedence over ExtUtils/typemap. (This is something that should probably be investigated further.)

        But it seems by using unmapped T_PTR you're getting the old T_PTR, not the new one, you can't redefine T_PTR, which makes sense.

        My T_PTR is the same as the original T_PTR, as there was nothing wrong with the original.
        I just had to change the typing of unsigned char* from T_PV to T_PTR. I could only make that switch for the unmapped type, which was a typedef of unsigned char*.
        Seems I won't need that typedef if I can get the two typemaps to load in the reverse order.

        I've just checked and found that I didn't need to provide the T_PTR "INPUT" and "OUTPUT" routines in my custom typemap ("nullmap.txt").
        That file needs only to consist of:
        unmapped T_PTR
        and the T_PTR "INPUT" and "OUTPUT" routines will be read from ExtUtils/typemap.
        Apologies - I did wonder about that at the time, but didn't spend the 10 seconds it takes to find out.
        'Twould have been much clearer if I had presented the one-line "nullmap.txt" instead of the multi-line version with its "INPUT" and "OUTPUT" sections.

        Cheers,
        Rob
Re^3: Inline::C and NULL pointers
by Marshall (Canon) on Dec 19, 2021 at 13:29 UTC
    Ok, I have been learning myself! Geez this question got me going this late evening... I have been having fun playing with this although verbose in my incremental discoveries. Nobody else was answering so I thought I'd have a go at it! The basic issue appears to be:
    FuncA("abc",undef,"xyz"); FuncA("abc",3,"xyz"); FuncA("abc","nmo","xyz +"); FuncB(1,undef,3); FuncB(1,"some string",3);
    C doesn't have the concept of "undef". It appears to me that you will have to tell C how undef should be interpreted - whether in this particular context it means a string, int or whatever. In the preceding, the C code would have to know whether undef as the second param means a "missing" string or an int. Maybe for an expected int, undef defaults to a zero? or maybe -1 or maybe 99999? But it appears to me that would have to be within the C code, not in any general mapping of Perl to C? Because undef could mean many different things to C depending upon the context. I don't know how SWIG would help?

    So as my first example post code shows, instead of char* in the C function definition, you need SV*. undef can be passed as an SV value. There is a C function to determine the exact Perl "type" of that SV value (I didn't show that, but there is such a function). My example just showed a simple test for "does this SV structure contain a Perl string or not?". I have seen more complex tests often with complicated || logic. But my first test code shows a simple test for whether the SV contains a valid string within its structure or not and how to deal with the result of that test.

    You will have to modify the C function definition to have a pointer to SV (SV*) instead of pointer to character (char*). As my code shows, if that SV doesn't have a valid string, set pointer equal to NULL. If that SV does contain a string, then extract the pointer to that string. When you have a simple char*, this translation is done automatically for you. But undef throws a wrinkle into the works.

    I am sure there are some really wild border cases like Perl passing, "123": That is a string but Perl would treat it as an int if used in a math operation.

    Yet another thought:
    As I think about this, you are copy and pasting this guy's C code into an inline::C Perl section mainly to reduce "start time". You want to call this guy's C code from Perl. But you don't want to modify this guy's code. So instead of calling this guy's code directly, you could write a "wrapper" C function, which you call from Perl. That C wrapper resolves these undef issues and then calls that guy's code. His code is never called with an undef value.

    You write:

    unsigned char *my_enctypex_msname(unsigned char *name, SV *ret){ this C code handles undef appropriately and then calls the "copy and paste" enctypex_msname(unsigned char *name, cha +r* ret){} }
    Your Perl code calls the "my function".
Re^3: Inline::C and NULL pointers
by NERDVANA (Deacon) on Dec 19, 2021 at 22:13 UTC
    It might be worth submitting a bug report to Inline::C about this. As an occasional user of Inline::C, I'm surprised that undefs wouldn't become NULL; that's the behavior I'd want too. I'm surprised I never ran into that before.
      I'm surprised that undefs wouldn't become NULL; that's the behavior I'd want too.

      The way I'm seeing it, the behaviour you want is not happening because of perl's default typemap.
      But if that's a bug, it's a Perl bug not an Inline::C bug.
      At least Inline::C provides the rope for one to change that behaviour.

      Cheers,
      Rob
        Strictly, it would not be a Perl bug but maybe an ExtUtils::ParseXS shortcoming. That's quite a slow-moving bit of code (e.g. on CPAN, it's 3.35 from Jul 2017; in Perl it's 3.44). Have you tried putting a new T_PTR definition in your own typemap?
        Since the purpose of Inline::C is to be a more friendly integration between the languages than XS, I think it's reasonable to ask Inline::C to do a little more work than perl's default typemap.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11139728]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (5)
As of 2024-04-25 10:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found