3

I am attempting to use ctypes to share a C++ object with Python, by creating the object in C++, and passing the pointer out to Python via a C wrapper. I want to be able to act on this object later using the other functions in the Python class, do_something in the code below.

I've tried the following code, but I get a segfault. I'm new to interfacing C with C++ and C with Python, so I'm not sure if I'm doing something fundamentally wrong when passing the pointer, or if the memory is being cleared/shifted by Python garbage-collection, after I create the object?

This question discusses a similar problem for boost, but the answer isn't very useful for ctypes.

object.h

class object {  
public:

  // constructor 
  object() { 
    pointer = nullptr;
  }

  // destructor
  virtual ~object() {
    delete pointer;
    pointer = nullptr;
  }

  // get member functions of object_pointer
  // from C++
  double do_something();

protected:

  // pointer to the object
  object_pointer *pointer;

};

extern "C" {
  object* object_new();
  void object_delete(object *Ob);
  double object_do_something(object *Ob);
}

object.cpp

#include "object.h"
double object::do_something() { return pointer->do_something(); }

extern "C" {
    object *object_new() { return new object(); }
    void object_delete(object *Ob) { delete Ob; }
    double object_do_something(object *Ob) { return Ob->do_something(); }
}

object.py

from ctypes import *

lib = cdll.LoadLibrary('./lib_object.so')
lib.object_new.argtypes = ()
lib.object_new.restype = c_void_p
lib.special_delete.argtypes = c_void_p,
lib.special_delete.restype = None
lib.object_pointer.argtypes = c_void_p
lib.object_pointer.restype = c_void_p

class Object:
    def __init__(self):
        self.obj = lib.object_new()
        print self.obj
    def __del__(self):
        lib.object_delete(self.obj)
    def do_something(self):
        lib.object_do_something(self.obj)

s = Object()
>> 94549743086144
s.do_something()
>> Segfault

Any help would be greatly appreciated!

Orca
  • 177
  • 9
  • What command did you use to compile object.cpp into lib_object.so? – pschill Feb 08 '19 at 14:24
  • 1
    Do you ever change `pointer`? Otherwise `pointer->do_something()` dereferences a nullpointer. Also, why is it `S->do_something()` and not `Ob->do_something`? – Artyer Feb 08 '19 at 15:06
  • Sorry the ```S->do_something()``` is a typo, I've fixed it – Orca Feb 08 '19 at 15:52
  • The pointer does get changed: an object is reconstructed from a character buffer, and then that object is pointed to by ```pointer``` – Orca Feb 08 '19 at 15:53
  • ***argtypes* should be a list** (or *tuple*): `lib.object_pointer.argtypes = [c_void_p]`. – CristiFati Feb 08 '19 at 22:04

1 Answers1

3

Notes:

  • None of the files (.cpp, .py) from the question compiled. They contain syntax errors and also semantic errors
  • I don't know what the pointer role was intended to be (it generated syntax errors). I can only assume that a singleton implementation was attempted
  • Considering the above, instead of pointing the errors (there are many of them) in the existing files, I'm creating a brand new basic example
  • Although ctypes is not the only area to improve, I'm also pointing it out: [Python 2]: ctypes - A foreign function library for Python

object.h:

class Object {
    public:

    Object() {
        m_double = 2.718282;
    }

    virtual ~Object() {}

    double do_something();

    private:

    double m_double;
};


extern "C" {
    Object* object_new();
    void object_delete(Object *ob);
    double object_do_something(Object *ob);
}

object.cpp:

#include <iostream>
#include "object.h"


using std::cout;

double Object::do_something() {
    std::cout << "Doing something in C++\n";
    return m_double;
}


extern "C" {
    Object *object_new() { return new Object(); }
    void object_delete(Object *pObj) { delete pObj; }
    double object_do_something(Object *pObj) { return pObj->do_something(); }
}

object.py:

#!/usr/bin/env python2

import sys
import ctypes


lib = ctypes.cdll.LoadLibrary('./lib_object.so')

lib.object_new.argtypes = []
lib.object_new.restype = ctypes.c_void_p
lib.object_delete.argtypes = [ctypes.c_void_p]
lib.object_do_something.argtypes = [ctypes.c_void_p]
lib.object_do_something.restype = ctypes.c_double


class Object:
    def __init__(self):
        self.obj = lib.object_new()
        print("`Object` instance (as a `void *`): 0x{:016X}".format(self.obj))

    def __del__(self):
        lib.object_delete(self.obj)

    def do_something(self):
        return lib.object_do_something(self.obj)


def main():
    obj = Object()
    ret = obj.do_something()
    print(ret)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

Output:

[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054594122]> ls
object.cpp  object.h  object.py
[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054594122]> gcc -shared -fPIC -o lib_object.so object.cpp -lstdc++
[cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054594122]> python2 object.py
Python 2.7.14 (default, Oct 31 2017, 21:12:13)
[GCC 6.4.0] on cygwin

`Object` instance (as a `void *`): 0x0000000600078870
Doing something in C++
2.718282
CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • Thank you, this is very helpful. I will try to provide a proper stand-alone example in the future, rather than pulling random bits from my code... – Orca Feb 09 '19 at 12:34
  • You're welcome! [\[SO\]: How to create a Minimal, Complete, and Verifiable example (mcve)](https://stackoverflow.com/help/mcve). – CristiFati Feb 11 '19 at 06:38