wlav / cppyy

Other
391 stars 40 forks source link

Could not convert argument to buffer or nullptr #126

Closed jameslewis4891 closed 1 year ago

jameslewis4891 commented 1 year ago

Hello,

I have been reading through the docs for the past few hours and cannot seem to get types aligned to make the following function call. The call to GetLibVers() and openUSBDevice are working fine

def get_printer_status():
    cppyy.include("CeSmLm.h")
    cppyy.load_library("libCeSmLm")

    printer = cppyy.gbl

    print(f"libCeSmLm version {printer.GetLibVers()}")

    open_printer_result = printer.openUSBDevice(0x0DD4, 0x0205, "")

    if open_printer_result == 0:
        recv_buffer = ctypes.create_string_buffer(2024)
        dw_size = ctypes.c_uint(2024)
        dw_read = ctypes.c_uint(0)
        result = printer.GetPrinterStatus(recv_buffer, dw_size, dw_read)
        y = 0

I get the following error

    result = printer.GetPrinterStatus(recv_buffer, dw_size, dw_read)
TypeError: unsigned long ::GetPrinterStatus(unsigned char* bufferRecv, const DWORD dwSize, DWORD* dwRead) =>
    TypeError: could not convert argument 1 (could not convert argument to buffer or nullptr)

I don't think this is a butg as such just myself struggling with aligning the types. Does anyone have any ideas? I only have the SO and header file for the library

Header File

/*
   This function the printer status
   Parameters:
   - buffer to store data in
   - size of the provided buffer
   - return parameter, where the function will store actually read data size

   Return values:
   - ERR_NOT_OPENED
   - ERR_IOCTL_SEND
   - ERR_IOCTL_PIPE
   - ERR_IOCTL_OVERFLOW
   - ERR_IOCTL_LOW_LEVEL
   - ERR_IOCTL_GENERAL
   - ERR_IOCTL_TIMEOUT
   - ERR_READ_BUFFER_FULL
   - ERR_READING_USB
   - SUCCESs

*/

DWORD GetPrinterStatus(unsigned char* bufferRecv,const DWORD dwSize,DWORD* dwRead);

Working example in C

  unsigned long dwRead = 0;
  unsigned long dwResult = 0;
  unsigned char buffer[READ_BUFFER_SIZE];

  dwResult = GetPrinterStatus(buffer, READ_BUFFER_SIZE, &dwRead);
wlav commented 1 year ago

ctypes.create_string_buffer uses ctypes.c_char as the underlying type, which is a signed char. The unsigned char type in ctypes is ctypes.c_ubyte.

I don't have access to the above code, but here's a working example:

import cppyy, ctypes

cppyy.cppdef(r"""\
void func(unsigned char* s) {
    s[0] = 'a'; s[1] = 'b'; s[2] = 'c';
}""");

recvbuf = (ctypes.c_ubyte*1024)()
cppyy.gbl.func(recvbuf)

for i in range(3):
    print(chr(recvbuf[i]))

It may seem pedantic to care about char signedness, but contrary to binding modern C++ code, low-level C code offers basically no clues to an automatic bindings generator to figure out allowable conversions. In particular, unsigned char is often not used as a character type and thus shouldn't (and isn't) be considered a string.

jameslewis4891 commented 1 year ago

Thanks @wlav progress. I'm now getting a *** Break *** segmentation violation

Code

def get_printer_status():
    cppyy.include("CeSmLm.h")
    cppyy.load_library("libCeSmLm")

    printer = cppyy.gbl

    print(f"libCeSmLm version {printer.GetLibVers()}")

    open_printer_result = printer.openUSBDevice(0x0DD4, 0x0205, "")

    if open_printer_result == 0:
        recv_buffer = (ctypes.c_ubyte*2024)()
        dw_size = 2024
        dw_read = 0
        result = printer.GetPrinterStatus(recv_buffer, dw_size, dw_read)
        y = 0

Result

libCeSmLm version 1.05
 *** Break *** segmentation violation
#0  0x00007f2e97b6445a in __GI___wait4 (pid=48122, stat_loc=stat_loc
entry=0x7fff2e23a228, options=options
entry=0, usage=usage
entry=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
#1  0x00007f2e97b6441b in __GI___waitpid (pid=<optimised out>, stat_loc=stat_loc
entry=0x7fff2e23a228, options=options
entry=0) at ./posix/waitpid.c:38
#2  0x00007f2e97acabcb in do_system (line=<optimised out>) at ../sysdeps/posix/system.c:171
#3  0x00007f2e97000725 in CppyyLegacy::TUnixSystem::StackTrace() () from /home/james/work/ckp80111/venv/lib/python3.10/site-packages/cppyy_backend/lib/libCoreLegacy.so
#4  0x00007f2e92704863 in (anonymous namespace)::do_trace (sig=1) at src/clingwrapper.cxx:239
#5  (anonymous namespace)::TExceptionHandlerImp::HandleException (this=<optimised out>, sig=1) at src/clingwrapper.cxx:252
#6  0x00007f2e96fff231 in CppyyLegacy::TUnixSystem::DispatchSignals(CppyyLegacy::ESignals) () from /home/james/work/ckp80111/venv/lib/python3.10/site-packages/cppyy_backend/lib/libCoreLegacy.so
#7  <signal handler called>
#8  0x00007f2e8ec01f71 in CComPortUsb::ReadDeviceIf0(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#9  0x00007f2e8ec01ad7 in CComPortUsb::GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#10 0x00007f2e8ec01763 in GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#11 0x00007f2e97108029 in ?? ()
#12 0x00007fff2e23d6c0 in ?? ()
#13 0x00007f2e9270c85f in WrapperCall (method=94659202771888, nargs=3, args_=0x7fff2e23d410, self=0x0, result=0x7fff2e23d410) at src/clingwrapper.cxx:885
#14 0x00007f2e9270ce17 in CallT<long long> (args=<optimised out>, nargs=<optimised out>, self=<optimised out>, method=<optimised out>) at src/clingwrapper.cxx:930
#15 Cppyy::CallLL (method=<optimised out>, self=<optimised out>, nargs=<optimised out>, args=<optimised out>) at src/clingwrapper.cxx:953
#16 0x00007f2e901f5111 in GILCallLL (ctxt=0x7fff2e23d6a0, self=<optimised out>, method=<optimised out>) at src/Executors.cxx:74
#17 CPyCppyy::(anonymous namespace)::ULongExecutor::Execute (this=<optimised out>, method=<optimised out>, self=<optimised out>, ctxt=0x7fff2e23d6a0) at src/Executors.cxx:273
#18 0x00007f2e901aecf9 in CPyCppyy::CPPMethod::ExecuteFast (self=<optimised out>, offset=<optimised out>, ctxt=0x7fff2e23d6a0, this=<optimised out>, this=<optimised out>) at src/CPPMethod.cxx:123
#19 0x00007f2e901b06c9 in CPyCppyy::CPPMethod::Execute (this=this
entry=0x5617908d73a0, self=self
entry=0x0, offset=offset
entry=0, ctxt=ctxt
entry=0x7fff2e23d6a0) at src/CPPMethod.cxx:892
#20 0x00007f2e901aac7f in CPyCppyy::CPPFunction::Call (this=0x5617908d73a0, self=
0x7fff2e23d638: 0x0, args=<optimised out>, nargsf=<optimised out>, kwds=<optimised out>, ctxt=0x7fff2e23d6a0) at src/CPPFunction.cxx:90
#21 0x00007f2e901b6441 in CPyCppyy::(anonymous namespace)::mp_vectorcall (pymeth=0x7f2e8fadec80, args=0x7f2e8fdeaee8, nargsf=9223372036854775811, kwds=0x0) at src/CPPOverload.cxx:641
#22 0x000056178ca25a72 in _PyEval_EvalFrameDefault ()
#23 0x000056178ca373ac in _PyFunction_Vectorcall ()
#24 0x000056178ca20005 in _PyEval_EvalFrameDefault ()
#25 0x000056178ca1c766 in ?? ()
#26 0x000056178cb14456 in PyEval_EvalCode ()
#27 0x000056178cb40f08 in ?? ()
#28 0x000056178cb39d5b in ?? ()
#29 0x000056178cb40c55 in ?? ()
#30 0x000056178cb40138 in _PyRun_SimpleFileObject ()
#31 0x000056178cb3fe33 in _PyRun_AnyFileObject ()
#32 0x000056178cb310ae in Py_RunMain ()
#33 0x000056178cb0734d in Py_BytesMain ()
#34 0x00007f2e97aa3d90 in __libc_start_call_main (main=main
entry=0x56178cb07310, argc=argc
entry=2, argv=argv
entry=0x7fff2e23dfe8) at ../sysdeps/nptl/libc_start_call_main.h:58
#35 0x00007f2e97aa3e40 in __libc_start_main_impl (main=0x56178cb07310, argc=2, argv=0x7fff2e23dfe8, init=<optimised out>, fini=<optimised out>, rtld_fini=<optimised out>, stack_end=0x7fff2e23dfd8) at ../csu/libc-start.c:392
#36 0x000056178cb07245 in _start ()
 *** Break *** segmentation violation
#0  0x00007f2e97b6445a in __GI___wait4 (pid=48340, stat_loc=stat_loc
entry=0x7fff2e23a228, options=options
entry=0, usage=usage
entry=0x0) at ../sysdeps/unix/sysv/linux/wait4.c:30
#1  0x00007f2e97b6441b in __GI___waitpid (pid=<optimised out>, stat_loc=stat_loc
entry=0x7fff2e23a228, options=options
entry=0) at ./posix/waitpid.c:38
#2  0x00007f2e97acabcb in do_system (line=<optimised out>) at ../sysdeps/posix/system.c:171
#3  0x00007f2e97000725 in CppyyLegacy::TUnixSystem::StackTrace() () from /home/james/work/ckp80111/venv/lib/python3.10/site-packages/cppyy_backend/lib/libCoreLegacy.so
#4  0x00007f2e927046e7 in (anonymous namespace)::do_trace (sig=1) at src/clingwrapper.cxx:239
#5  (anonymous namespace)::TExceptionHandlerImp::HandleException (this=<optimised out>, sig=1) at src/clingwrapper.cxx:258
#6  0x00007f2e96fff231 in CppyyLegacy::TUnixSystem::DispatchSignals(CppyyLegacy::ESignals) () from /home/james/work/ckp80111/venv/lib/python3.10/site-packages/cppyy_backend/lib/libCoreLegacy.so
#7  <signal handler called>
#8  0x00007f2e8ec01f71 in CComPortUsb::ReadDeviceIf0(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#9  0x00007f2e8ec01ad7 in CComPortUsb::GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#10 0x00007f2e8ec01763 in GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#11 0x00007f2e97108029 in ?? ()
#12 0x00007fff2e23d6c0 in ?? ()
#13 0x00007f2e9270c85f in WrapperCall (method=94659202771888, nargs=3, args_=0x7fff2e23d410, self=0x0, result=0x7fff2e23d410) at src/clingwrapper.cxx:885
#14 0x00007f2e9270ce17 in CallT<long long> (args=<optimised out>, nargs=<optimised out>, self=<optimised out>, method=<optimised out>) at src/clingwrapper.cxx:930
#15 Cppyy::CallLL (method=<optimised out>, self=<optimised out>, nargs=<optimised out>, args=<optimised out>) at src/clingwrapper.cxx:953
#16 0x00007f2e901f5111 in GILCallLL (ctxt=0x7fff2e23d6a0, self=<optimised out>, method=<optimised out>) at src/Executors.cxx:74
#17 CPyCppyy::(anonymous namespace)::ULongExecutor::Execute (this=<optimised out>, method=<optimised out>, self=<optimised out>, ctxt=0x7fff2e23d6a0) at src/Executors.cxx:273
#18 0x00007f2e901aecf9 in CPyCppyy::CPPMethod::ExecuteFast (self=<optimised out>, offset=<optimised out>, ctxt=0x7fff2e23d6a0, this=<optimised out>, this=<optimised out>) at src/CPPMethod.cxx:123
#19 0x00007f2e901b06c9 in CPyCppyy::CPPMethod::Execute (this=this
entry=0x5617908d73a0, self=self
entry=0x0, offset=offset
entry=0, ctxt=ctxt
entry=0x7fff2e23d6a0) at src/CPPMethod.cxx:892
#20 0x00007f2e901aac7f in CPyCppyy::CPPFunction::Call (this=0x5617908d73a0, self=
0x7fff2e23d638: 0x0, args=<optimised out>, nargsf=<optimised out>, kwds=<optimised out>, ctxt=0x7fff2e23d6a0) at src/CPPFunction.cxx:90
#21 0x00007f2e901b6441 in CPyCppyy::(anonymous namespace)::mp_vectorcall (pymeth=0x7f2e8fadec80, args=0x7f2e8fdeaee8, nargsf=9223372036854775811, kwds=0x0) at src/CPPOverload.cxx:641
#22 0x000056178ca25a72 in _PyEval_EvalFrameDefault ()
#23 0x000056178ca373ac in _PyFunction_Vectorcall ()
#24 0x000056178ca20005 in _PyEval_EvalFrameDefault ()
#25 0x000056178ca1c766 in ?? ()
#26 0x000056178cb14456 in PyEval_EvalCode ()
#27 0x000056178cb40f08 in ?? ()
#28 0x000056178cb39d5b in ?? ()
#29 0x000056178cb40c55 in ?? ()
#30 0x000056178cb40138 in _PyRun_SimpleFileObject ()
#31 0x000056178cb3fe33 in _PyRun_AnyFileObject ()
#32 0x000056178cb310ae in Py_RunMain ()
#33 0x000056178cb0734d in Py_BytesMain ()
#34 0x00007f2e97aa3d90 in __libc_start_call_main (main=main
entry=0x56178cb07310, argc=argc
entry=2, argv=argv
entry=0x7fff2e23dfe8) at ../sysdeps/nptl/libc_start_call_main.h:58
#35 0x00007f2e97aa3e40 in __libc_start_main_impl (main=0x56178cb07310, argc=2, argv=0x7fff2e23dfe8, init=<optimised out>, fini=<optimised out>, rtld_fini=<optimised out>, stack_end=0x7fff2e23dfd8) at ../csu/libc-start.c:392
#36 0x000056178cb07245 in _start ()

Process finished with exit code 129 (interrupted by signal 1: SIGHUP)
wlav commented 1 year ago

That segfault is inside another function that is called by the C++ function that you're calling:

#8  0x00007f2e8ec01f71 in CComPortUsb::ReadDeviceIf0(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#9  0x00007f2e8ec01ad7 in CComPortUsb::GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so
#10 0x00007f2e8ec01763 in GetPrinterStatus(unsigned char*, unsigned long, unsigned long*) () from /home/james/work/ckp80111/./libCeSmLm.so

I'm not excluding (yet) that there's a problem with the argument conversion, given that all 3 functions have the same arguments, and are thus most likely simply passed through.

However, my first guess is that the 3rd argument isn't allowed to be nullptr, as used here:

        dw_read = 0
        result = printer.GetPrinterStatus(recv_buffer, dw_size, dw_read)

Point being that your original example passed a pointer to a null value, not a nullptr:

  unsigned long dwRead = 0;
  dwResult = GetPrinterStatus(buffer, READ_BUFFER_SIZE, &dwRead);

This is again a problem with these low level functions not exposing exactly what you intend, but also that a Python int can not be an outparam, so the best automatic guess here is that the intended conversion was to nullptr. Instead, use ctypes again:

      dwread = ctypes.c_ulong(0)
      result = printer.GetPrinterStatus(recv_buffer, dw_size, dw_read)

where this time to intention is clear: if you go through the trouble of creating a ctypes parameter, it's virtually certain intended to be an outparam, so the conversion to a pointer type is automatic. Alternatively, you can explicitly use ctypes.pointer(dw_read)) if you prefer that for readability.

jameslewis4891 commented 1 year ago

That did the trick thank you very much for your help @wlav