best "void" return of a member function

Discussion in 'Python' started by Andreas Otto, Apr 20, 2009.

  1. Andreas Otto

    Andreas Otto Guest

    Hi,

    I'm writing a native language binding for a library.

    http://libmsgque.sourceforge.net/

    Every native method called by PYTHON have to return
    a PyObject* even if the function itself does not
    return anything.

    I have 2 possibilities for return a PyObject*


    1. the first argument of the method function
    -> return self;
    2. an entire new empty object
    -> return Py_BuildValue("");

    Question: what is the best return statement for a "void" function ?

    P.S: NULL is not allowed, because NULL is returned in the case
    of an "error"


    mfg

    Andreas Otto
     
    Andreas Otto, Apr 20, 2009
    #1
    1. Advertisements

  2. Andreas Otto

    Andreas Otto Guest

    Andreas Otto wrote:

    well propable found the answer by my own ...

    Py_RETURN_NONE

    should be the best
     
    Andreas Otto, Apr 20, 2009
    #2
    1. Advertisements

  3. Hmmm, this sounds like your goal is to write an exact 1:1 wrapper of the C
    library API (for which there are tools like SWIG&friends). If the library
    happens to have a rather unusual, close-to object oriented, high-level C API,
    that might work. Otherwise, you might want to try to wrap it in a more Pythonic
    look&feel style, that wraps operations and use-cases rather than plain
    functions. That should make it easier to hide things like memory allocation and
    other C implementation details from users, and will generally increase the
    performance of your binding, as it will require less calls for larger operations
    in C space.

    Stefan
     
    Stefan Behnel, Apr 20, 2009
    #3
  4. Here is an example from your web page:

    Sending data is a sequence of commands to prepare a data-package
    and one command to send this package.

    // get the "send" object from the "msgque" object
    struct MqSendS * const send = msgque->send;
    // init the data-package
    MqSendSTART (send);
    // fill the data-package with data
    MqSendI (send, myInteger);
    MqSendC (send, "myString");
    // send the data-package to the server
    MqSendEND (send, "IDNT", NULL);

    A first thought would be a class "Connection" and a method "send_package" that
    takes an arbitrary number of positional arguments, such as

    connection = some_source.connect()
    connection.send_package(my_int, "myString", end_id="IDNT")

    In case you do not know the required data type packing (which is explicit in the
    C example), you can either wrap the types in some kind of typing construct
    (which might degrade performance, unless you know that you do not need it in
    most cases), or define message packing formats in advance in some way, e.g.
    similar to Python's "array" module.

    Just an idea. Since you appear to be the main author of that library, I assume
    that you know best how to make it usable.

    Stefan
     
    Stefan Behnel, Apr 20, 2009
    #4
  5. I (obviously ;) meant the format identifiers in the "struct" module here.

    http://docs.python.org/library/struct.html

    Stefan
     
    Stefan Behnel, Apr 20, 2009
    #5
  6. Andreas Otto

    Andreas Otto Guest

    Thanks for your help ..

    I'm almost finished ... it took me ~1week from a non Python developer to:
    1. download, install python
    2. learn how to use python, syntax, class, objects, protocol, ...
    3. learn how to use the native interface, ~hundreds of C functions
    4. finally create a project add my native code, compile, build ... test
    -> I just add one extra type I call them "PyMqS_type"

    all the special tools are not necessary because
    if you wrote one language interface you can write every language interface
    -> the tasks are allways the same... just the language-specific-names
    are changing


    mfg

    Andreas Otto
     
    Andreas Otto, Apr 20, 2009
    #6
  7. Andreas Otto

    Andreas Otto Guest

    Propable you can help me with an other problem ...

    the following code crash with:

    ==31431== Process terminating with default action of signal 11 (SIGSEGV)
    ==31431== General Protection Fault
    ==31431== at 0x4EA5151: PyObject_GenericGetAttr (object.c:982)
    ==31431== by 0x4EF1FBD: PyEval_EvalFrameEx (ceval.c:1941)
    ==31431== by 0x4EF5261: PyEval_EvalCodeEx (ceval.c:2869)
    ==31431== by 0x4E91618: function_call (funcobject.c:628)
    ==31431== by 0x4E6C7AC: PyObject_Call (abstract.c:2161)
    ==31431== by 0x4E8055A: method_call (classobject.c:323)
    ==31431== by 0x4E6C7AC: PyObject_Call (abstract.c:2161)
    ==31431== by 0x4E6DFFE: PyObject_CallFunctionObjArgs (abstract.c:2392)
    ==31431== by 0x6B8A43B: Python_pymsgque_pCallVoidMethod (pymisc.c:36)
    ==31431== by 0x6B8991D: PythonChildCreate (pymsgque.c:89)
    ==31431== by 0x6D9E15F: pTokenCheckSystem (token.c:547)
    ==31431== by 0x6DA5110: pReadHDR (read.c:385)

    with the following scenario

    1. I have an object on an class
    2. this object has a callable method context->config
    3. I create an additional object of the same class
    4. now I want to bind the context->config to the new object
    5. I use:
    context->config = PyMethod_New(PyMethod_GET_FUNCTION(context->config),
    (PyObject*)context->self);
    6. but call this object with "PyObject_CallFunctionObjArgs" create a crash

    this is my C code with some helper

    if (context->self == NULL) {
    context->self = PyObject_New(PyMqS_Obj,(PyTypeObject*)context->class);
    M0
    printO(PyMethod_Function(context->config))
    if (context->config != NULL)
    context->config = PyMethod_New(PyMethod_GET_FUNCTION(context->config),
    (PyObject*)context->self);
    M1
    if (context->create != NULL)
    context->create = PyMethod_New(PyMethod_GET_FUNCTION(context->create),
    (PyObject*)context->self);
    M2
    printO(context->config)
    printO(PyObject_Type(context->config))
    }

    // 4. set the 'hdl'
    context->self->msgque = msgque;

    // 5. init the new object
    M3
    if (msgque->config.server == MQ_YES && context->config != NULL) {
    switch (NS(pCallVoidMethod)(msgque, context->config, NULL)) {
    case MQ_OK: break;
    case MQ_CONTINUE:
    case MQ_ERROR: goto error;
    }
    }
    M4

    this is the output:

    output from the first object without error:
    configO -> <bound method Server.config of <__main__.Server object at
    0x2acee8eb7830>>
    PyObject_Type(configO) -> <class 'method'>
    PythonChildCreate(pymsgque.c:87) -> 33333333333333333
    PythonChildCreate(pymsgque.c:95) -> 44444444444444444

    output from the second object with the crash:
    PythonChildCreate(pymsgque.c:71) -> 00000000000000000
    PyMethod_Function(context->config) -> <function config at 0x2acee8ead8d0>
    PythonChildCreate(pymsgque.c:75) -> 11111111111111111
    PythonChildCreate(pymsgque.c:78) -> 22222222222222222
    context->config -> <bound method Server.config of <__main__.Server object at
    0x2acee8a67b20>>
    PyObject_Type(context->config) -> <class 'method'>
    PythonChildCreate(pymsgque.c:87) -> 33333333333333333
     
    Andreas Otto, Apr 20, 2009
    #7
  8. This is like saying: if you used one programming language, you can use every
    programming language. "Use" is different from "master" or "appreciate".
    That's the typical SWIG problem: you can generate wrappers for tons of
    languages, mostly automatically. But none of them will feel 'native' to the
    users of each of the target languages (well, possibly excluding C and Java here).

    As the author, you write a wrapper once (and maybe keep maintaining it), but
    every user of the wrapper will have to get along with its API that was copied
    into his/her language from another one. And there are usually a lot more users
    than authors.

    I'm not undervaluing your work. It's good to have many, many library bindings
    for Python. But having a "good" one would be even nicer.

    Stefan
     
    Stefan Behnel, Apr 20, 2009
    #8
  9. Andreas Otto

    Terry Reedy Guest

    To provide a Pythonic interface, error returns should often be converted
    to Python exceptions.
     
    Terry Reedy, Apr 20, 2009
    #9
  10. Andreas Otto

    Tim Rowe Guest

    On the other hand, a thin wrapper around a library will let the user
    leverage existing library documentation, so the choice is not a
    complete no-brainer.

    One approach (used in the Ada community for it's MS Windows bindings,
    for instance) is to have a thin wrapper and build the thick wrappers
    on top of that. Most Ada MS Windows thick wrappers (ie, ones that feel
    natural to Ada) are built on top of Win32Ada, which stays as close to
    the underlying C interface as it can.
     
    Tim Rowe, Apr 22, 2009
    #10
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.