Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re^7: Perl XS binding to a struct with an array of chars*

by Marshall (Canon)
on Nov 26, 2022 at 02:32 UTC ( [id://11148396]=note: print w/replies, xml ) Need Help??


in reply to Re^6: Perl XS binding to a struct with an array of chars*
in thread Perl XS binding to a struct with an array of chars*

struct _Edje_Message_String_Set { int count; char * str[]; };
Is it possible to handle that ?

Yes, it sure is!

I actually decided that this is the best way to implement the idea of the array of pointer within the struct itself. My reasoning is that the [] gives a big clue that there is no storage in the struct at all for the array of pointers to strings.

In my implementation below, I allocated one more slot for a NULL pointer. That is so that more traditional C pointer iteration style can be used as an alternative to using some counter based upon the "count".

Also not that the Perl print statements come before the C print statements! That of course has to be due some buffering weirdness, but I didn't figure out how to defeat that behaviour. Also for some reason, the API function, av_count() wasn't available on my version so a simple workaround calculation was used.

#MessageStorageInsideStruct 11/25/2022 # #https://www.perlmonks.org/?node_id=11148268 # # # use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1, CLEAN_AFTER_BUILD => 1, USING => 'ParseRegExp', ; use Inline "C"; struct_size(); my @in = ("hello foo", "hello bar","hello world", "goodbye", '1', '2', + '3'); foo(\@in); print "back inside Perl program!!!!\n"; print "printout is not in time order you expected!\n\n"; =EXAMPLE RUN back inside Perl program!!!! printout is not in time order you expected! Size of _Edje_Message_String_Set structure: 8 bytes Address of the Message Struct: 000000000308FAB8 Address of the Pointer Array: 000000000308FAC0 setting element 0 string: hello foo's pointer is at Address 000000000308FAC0 setting element 1 string: hello bar's pointer is at Address 000000000308FAC8 setting element 2 string: hello world's pointer is at Address 000000000308FAD0 setting element 3 string: goodbye's pointer is at Address 000000000308FAD8 setting element 4 string: 1's pointer is at Address 000000000308FAE0 setting element 5 string: 2's pointer is at Address 000000000308FAE8 setting element 6 string: 3's pointer is at Address 000000000308FAF0 Dumping an EdjeMessageStringSet Count = 7 1) hello foo 2) hello bar 3) hello world 4) goodbye 5) 1 6) 2 7) 3 Destroying an EdjeMessageStringSet =cut __END__ __C__ /*********** Start of C Code ********/ struct _Edje_Message_String_Set { int count; //On 64 bit machine, this is 8 bytes char *str[]; //str has no "size" and is not counted in sizeof(_Edj +e_Message_String_Set) }; typedef struct _Edje_Message_String_Set EdjeMessageStringSet; void struct_size(void) { printf("Size of _Edje_Message_String_Set structure: %d bytes\n", sizeof(EdjeMessageStringSet) ); } /************/ EdjeMessageStringSet * _new(AV* val_arr) { int count = av_len(val_arr) + 1; // newer av_count() not avail thi +s version // sizeof(Edje_Message_String_Set) only has the integer,count of 8 b +ytes // space for the array of pointers must be allocated by safemalloc // remember to add one more slot for a NULL pointer EdjeMessageStringSet* message = (EdjeMessageStringSet*) safemalloc ( sizeof(EdjeMessageStringSet) + ( +count+1)*sizeof(char*) ); printf ("Address of the Message Struct: %p\n",message); + if(message == NULL) croak("Failed to allocate memory for message in _new function") +; message->count = count; char** p = &(message->str[0]); printf ("Address of the Pointer Array: %p\n", p); int i; for(i= 0; i < message->count; i++) { printf ("setting element %d\n",i); SV** elem = av_fetch(val_arr, i, 0); if (elem==NULL) croak ("bad SV elem value in _new function"); char* string = SvPVutf8_nolen(*elem); printf ("string: %s's pointer is at Address %p\n",string,p); *p++ = savepv(string); } *p = NULL; //Can use either count or NULL pointer as a loop variab +le return message; } /******************/ void _iterate (EdjeMessageStringSet* m) { printf ("Dumping an EdjeMessageStringSet\n"); printf ("Count = %d\n",m->count); int i = 1; char** p = &(m->str[0]); while (*p) {printf ("%d) %s\n",i++,*p++);} } void DESTROY(EdjeMessageStringSet* m) { printf ("Destroying an EdjeMessageStringSet\n"); char** p = &(m->str[0]); while(*p){Safefree(*p++);} //zap eaxh cloned string Safefree(m); //zap main structure } /************/ void foo(AV * arref) { EdjeMessageStringSet* m = _new(arref); _iterate(m); DESTROY(m); }

Replies are listed 'Best First'.
Re^8: Perl XS binding to a struct with an array of chars*
by syphilis (Archbishop) on Nov 27, 2022 at 05:23 UTC
    Nice work !!
    I'll draw attention to an oddity I've just noticed, of perhaps little significance.
    Regarding the struct definition and typedef:
    struct _Edje_Message_String_Set { int count; //On 64 bit machine, this is 8 bytes char *str[]; //str has no "size" and is not counted in sizeof(_Edj +e_Message_String_Set) }; typedef struct _Edje_Message_String_Set EdjeMessageStringSet;
    The 'int' type is always 4 bytes on Windows, irrespective of architecture. And I think it's generally the same case on Linux.
    IIRC, on Linux, it's usually the size of the 'long int' that varies with architecture - but 'int' usually stays at 4 bytes.

    On 64 bit Windows, I'm finding that if char * str[]; is removed from the struct, then struct size is 4 bytes.
    If char * str[]; is included, then the struct size is 8 bytes.
    So it seems that "str" is increasing the size of the struct by 4 bytes. But that doesn't seem right to me.

    Here's the demo I used:
    use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1, USING => 'ParseRegExp', ; use Inline C =><<'EOC'; typedef struct _foo { int count; } foo; typedef struct _bar { int count; char *str[]; } bar; void sizes(void) { printf( "FOO: %d %d\n", sizeof(struct _foo), sizeof( foo) ); printf( "BAR: %d %d\n", sizeof(struct _bar), sizeof( bar) ); printf( "INTSIZE: %d\n", sizeof(int) ); } EOC sizes(); __END__ On 64 bit windows, outputs: FOO: 4 4 BAR: 8 8 INTSIZE: 4
    Maybe a bug in xsubpp ? Nope - it's just something that 64-bit gcc does on both Windows and Ubuntu. If it's a bug, then it's a gcc bug.

    On 32-bit windows, it seems that char *str[]; does indeed make zero contribution to the size of the struct, and the same script outputs:
    FOO: 4 4 BAR: 4 4 INTSIZE: 4
    Cheers,
    Rob
      I was surprised to learn that size of int is 32 bits even on a 64 bit compiler. But that is true. To get 64 bit int, even long long is required on some compilers.

      What I found out is that default for the 64 bit gcc compiler is to enable padding to maintain dword memory alignment in structures. This is what is causing the seemingly weird results with your sizeof() test code. sizeof() can return more than the obvious number of bytes due to padding. See Data Alignment in Structs for some more info. There is a way in gcc to override this behavior if perhaps you need to match some weird binary structure exactly verbatim. Just like malloc(), struct address assignment "likes" dword (64 bit alignment).

      typedef struct _foo { int count; //with just int, sizeof() is 4 bytes char anything; //forces sizeof() increase from 4 to 8 bytes! } foo;
      Evidently, even putting something in the struct that has no storage, like char* pointer[], forces alignment bytes to be added. The mysterious extra 4 bytes aren't anything, they are just junk padding bytes. The pointer(s) when added will be 8 bytes and it is highly desirable for these to be fully contained on a single memory row. If they are on 32 bit boundaries, the hardware can still read them, but at a performance penalty.

      So my code as written performs correctly, however the explanation of why the struct is 8 bytes is not correct. 4 bytes are for the integer (not 8 as I wrongly assumed) but then an additional 4 bytes are added as padding. 4+4=8. So the entire array of pointer is 64 bit aligned (8 byte boundary).

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2024-04-18 20:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found