Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #109552 > unrolled thread

Trying to pass sys.argv as (int argc, char **argv) using ctypes

Started byMark Summerfield <list@qtrac.plus.com>
First post2016-06-06 02:50 -0700
Last post2016-06-06 07:08 -0700
Articles 3 — 2 participants

Back to article view | Back to comp.lang.python


Contents

  Trying to pass sys.argv as (int argc, char **argv) using ctypes Mark Summerfield <list@qtrac.plus.com> - 2016-06-06 02:50 -0700
    Re: Trying to pass sys.argv as (int argc, char **argv) using ctypes eryk sun <eryksun@gmail.com> - 2016-06-06 13:47 +0000
      Re: Trying to pass sys.argv as (int argc, char **argv) using ctypes Mark Summerfield <list@qtrac.plus.com> - 2016-06-06 07:08 -0700

#109552 — Trying to pass sys.argv as (int argc, char **argv) using ctypes

FromMark Summerfield <list@qtrac.plus.com>
Date2016-06-06 02:50 -0700
SubjectTrying to pass sys.argv as (int argc, char **argv) using ctypes
Message-ID<6a5f4f7b-37e1-42dd-b024-be3411659930@googlegroups.com>
Hi,

I have a setup roughly like this:

import ctypes
import sys

Lib = ctypes.cdll.LoadLibrary("libthing")

c_char_pp = ctypes.POINTER(ctypes.c_char_p)

LibOpen = Lib.Open
LibOpen.argtypes = (ctypes.c_int,       # argc
                    c_char_pp)          # argv
LibOpen.restype = ctypes.c_int


argc = ctypes.c_int(len(sys.argv))
Args = ctypes.c_char_p * len(sys.argv)
args = Args(*[ctypes.c_char_p(arg.encode("utf-8"))
              for arg in sys.argv])
argv = ctypes.pointer(args)
LibOpen(argc, ctypes.byref(argv))

But when run it produces an error:

ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_char_p instance instead of pointer to LP_c_char_p_Array_1

or this if I use LibOpen(argc, argv):

ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_char_p instance instead of LP_c_char_p_Array_1


I understand what it is telling me; but I don't know or understand ctypes well enough to fix it. I guess I need to convert the array pointer to a char ** pointer? Can anyone tell me, or point me to info that would help?

Thanks!

[toc] | [next] | [standalone]


#109563

Fromeryk sun <eryksun@gmail.com>
Date2016-06-06 13:47 +0000
Message-ID<mailman.36.1465220874.2306.python-list@python.org>
In reply to#109552
On Mon, Jun 6, 2016 at 9:50 AM, Mark Summerfield <list@qtrac.plus.com> wrote:
>
> Lib = ctypes.cdll.LoadLibrary("libthing")

Use ctypes.CDLL('libthing'). It's simpler, plus it's the only way to
pass the handle, mode, and use_errno parameters if you need them.

> LibOpen.restype = ctypes.c_int

This line isn't required because c_int is the default result type.

> argc = ctypes.c_int(len(sys.argv))

ctypes automatically converts integer arguments that are passed by
value. You only need to create a c_int when you're passing by
reference.

> Args = ctypes.c_char_p * len(sys.argv)

The argv array in ANSI C should have length `argc + 1`. There's a
final null terminator, even though it's redundant given the argc
count.

> args = Args(*[ctypes.c_char_p(arg.encode("utf-8"))
>               for arg in sys.argv])

There's no need to create a list just to unpack it. You can use a for
loop to populate the array directly. Also, you don't have to manually
instantiate c_char_p instances when storing the elements of a c_char_p
array; the base type's getfunc will convert Python byte strings.

Also, since the function signature isn't `const char **`, you should
use ctypes.create_string_buffer just in case the function expects to
be able to modify the argv strings. This requires switching from using
c_char_p to a more generic POINTER(c_char).

> argv = ctypes.pointer(args)
> LibOpen(argc, ctypes.byref(argv))

C array arguments are passed as a pointer to the first element. You
don't have to manually create a pointer. And if you do, certainly you
shouldn't pass it by reference. That mistakenly passes a pointer to
the pointer to the first element of the argv array (which is a pointer
to the first character in the first string argument). Naturally you
get the following error:

> expected LP_c_char_p instance instead of pointer to LP_c_char_p_Array_1

Due to limitations in ctypes you also get the following error when you
pass the pointer by value:

> expected LP_c_char_p instance instead of LP_c_char_p_Array_1

Only passing the array directly is special cased for this to work.

Try the following code:

    import sys
    import ctypes

    lib = ctypes.CDLL('libthing')

    LP_c_char = ctypes.POINTER(ctypes.c_char)
    LP_LP_c_char = ctypes.POINTER(LP_c_char)

    lib.LibOpen.argtypes = (ctypes.c_int, # argc
                            LP_LP_c_char) # argv

    argc = len(sys.argv)
    argv = (LP_c_char * (argc + 1))()
    for i, arg in enumerate(sys.argv):
        enc_arg = arg.encode('utf-8')
        argv[i] = ctypes.create_string_buffer(enc_arg)

    lib.LibOpen(argc, argv)

[toc] | [prev] | [next] | [standalone]


#109565

FromMark Summerfield <list@qtrac.plus.com>
Date2016-06-06 07:08 -0700
Message-ID<fccc43e3-828b-4444-8320-e1db1c4e2da1@googlegroups.com>
In reply to#109563
Thanks, that works! And also thanks for the excellent explanations of each part.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web