wlav / cppyy

Other
387 stars 39 forks source link

char * arrays don't work like they used to (3.0.0 vs 2.4.2) #193

Closed myusernameisunique closed 8 months ago

myusernameisunique commented 9 months ago

It seems like the behavior of char arrays (char[], char, char[][], char[]) has changed in version 3.0.0 as compared to version 2.4.2. Here is the code to reproduce:

import cppyy
cppyy.cppdef(r"""
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Foo {
    char val[10], *ptr;
    char values[2][10], *pointers[2];
};
void set_pointers_and_print_results(struct Foo * foo)
{
    // populate arrays
    strcpy(foo->val, "howdy!");
    strcpy(foo->values[0], "hello");
    strcpy(foo->values[1], "world!");

    // set pointers
    foo->ptr = foo->val;
    foo->pointers[0] = foo->values[0];
    foo->pointers[1] = foo->values[1];

    // print results (from c)
    printf("print from c:\n");
    printf("val= %s\n", foo->val);
    printf("ptr= %s\n", foo->ptr);
    printf("values= %s %s\n", foo->values[0], foo->values[1]);
    printf("pointers= %s %s\n", foo->pointers[0], foo->pointers[1]);
}
""")

foo = cppyy.gbl.Foo()
cppyy.gbl.set_pointers_and_print_results(foo)

# print results (from python)
print("\nprint from python:")
print("val=", foo.val)
print("ptr=", foo.ptr)
print("values=", foo.values[0], foo.values[1])
print("pointers=", foo.pointers[0], foo.pointers[1])

Here is the result from version 2.4.2 (this one successfully prints the pointers):

print from c:
val= howdy!
ptr= howdy!
values= hello world!
pointers= hello world!

print from python:
val= howdy!
ptr= howdy!
values= <cppyy.LowLevelView object at 0x7f5f01762e30> <cppyy.LowLevelView object at 0x7f5efa6fdeb0>
pointers= hello world!

Here is the result from version 3.0.0 (version 1 of 2 completes without error, but prints garbage for pointers):

print from c:
val= howdy!
ptr= howdy!
values= hello world!
pointers= hello world!

print from python:
val= <cppyy.LowLevelView object at 0x7f6ee5eaa7b0>
ptr= howdy!
values= hello world!
pointers= x <

Here is the result from version 3.0.0 (version 2 of 2 fails with OverflowError):

print from c:
val= howdy!
ptr= howdy!
values= hello world!
pointers= hello world!

print from python:
val= <cppyy.LowLevelView object at 0x7fc75a31e7b0>
ptr= howdy!
values= hello world!
Traceback (most recent call last):
  File "//repro.py", line 39, in <module>
    print("pointers=", foo.pointers[0], foo.pointers[1])
OverflowError: character argument not in range(0x110000)

I need a way to create (and use) an array of char pointers. Can you please take a look?

Thank you!

wlav commented 8 months ago

Alright, that was a convoluted one, but things should now be way more consistent and the example above works, except that both val and values now return LowLevelViews. This for consistency, although I realize that that's not the old behavior. The reason for keeping them LowLevelViews, is that there's no reason to assume that a char[] array is in fact a null-terminated string, but moreover, by copying into a Python str, any elements beyond a possible '\0' become inaccessible (and can't, for example, be assigned to), the sub-array itself can't be passed back to C++ via a pointer, etc.

To make conversion to C strings easier, I added an as_string() method (works only for 1-dim char arrays) and 1-dim slicing.

wlav commented 8 months ago

Released with release 3.1.0. Feel free to reopen if you still have issues.

myusernameisunique commented 8 months ago

Great! I will try out version 3.1.0 this week hopefully. It makes sense to me that both val and values should return LowLevelViews. I will re-open if I find anything else. Thank you!