1

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
ErdoganO
  • 71
  • 4

1 Answers1

0

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
Wim Lavrijsen
  • 3,453
  • 1
  • 9
  • 21
  • Thanks for the comment. Unfortunately casting another type is not a good solution for me. Getting LowLevelView is not my main problem. I want to get-set the value of the pointer. `cppyy.ll.cast["std::byte*"](char_ptr)[0] = -1` produces `ValueError: integer to character: value -5 not in range [0,255]` – ErdoganO Mar 22 '22 at 05:39
  • In that case, ideally an `int8_t` should be used. I know that doesn't work (Cling does not provide the unresolved typedef, so it resolved to `char` before binding happens). Above as been updated with an alternative workaround. Aside, you could also assign `-1 % 256`. – Wim Lavrijsen Mar 22 '22 at 17:09