cppyy.ll.cast fails to cast char*

197 Views Asked by At

Let's assume we have a char* named chr_ptr from some external library.

I tried to cast this pointer to char* by using cppyy.ll.cast["char*"](chr_ptr)

Expected: cppyy.LowLevelView

Actual: str

Other datatypes works well, this problem only occurs char(a.k.a. int8_t). Casting the pointer (let's say) int16_t is OK.

import cppyy
import cppyy.ll

if not hasattr(cppyy.gbl, "get_char_pointer"):
    cppyy.cppdef("""
    void* get_char_pointer()
    {
        char a = 1;
        return &a;
    }
    """)
if not hasattr(cppyy.gbl, "get_uint16_t_pointer"):
    cppyy.cppdef("""
    void* get_uint16_t_pointer()
    {
        uint16_t a = 1;
        return &a;
    }
    """)

if not hasattr(cppyy.gbl, "print_address"):
    cppyy.cppdef("""
    void print_address(void* p)
    {
        std::cout << "address is: " << p << std::endl;
    }
    """)

char_ptr = cppyy.gbl.get_char_pointer()
uint16t_ptr = cppyy.gbl.get_uint16_t_pointer()

cppyy.gbl.print_address(char_ptr)
cppyy.gbl.print_address(uint16t_ptr)

casted_char_ptr = cppyy.ll.cast["char*"](char_ptr)  # At this point expected cppyy.LowLevelView but I got str
casted_uint16t_ptr = cppyy.ll.cast["uint16_t*"](uint16t_ptr)

print()
print(type(casted_char_ptr))
print(type(casted_uint16t_ptr))

print()
try:
    cppyy.gbl.print_address(casted_char_ptr)
except TypeError as err:
    print(err)
cppyy.gbl.print_address(casted_uint16t_ptr)

The result is:

address is: 0x7fff58b64847
address is: 0x7fff58b64846

<class 'str'>
<class 'cppyy.LowLevelView'>

void ::print_address(void* p) =>
    TypeError: could not convert argument 1
address is: 0x7fff58b64846
1

There are 1 best solutions below

2
On

Yes, char* is one of those types that solely based on reflection information isn't clear in its intended use. There's a half-finished patch to allow fixing up functions/data members with what their unique meaning of char* happens to be, so that's coming, and yes, cast() is a clear case where char* more likely means byte* than C-string, so should have that as default.

That said, in this case, it should be fine to actually use std::byte* (or unsigned char* if you're not using C++17) to indicate what the result should be:

casted_char_ptr = cppyy.ll.cast["std::byte*"](char_ptr) 

which produces the LowLevelView as desired.

Edit: workaround for signed byte:

cppyy.cppdef("enum signed_byte : signed char;")
casted_char_ptr = cppyy.ll.cast["signed_byte*"](char_ptr)
casted_char_ptr[0] = -1