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!

5
  • What command did you use to compile object.cpp into lib_object.so? Commented Feb 8, 2019 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? Commented Feb 8, 2019 at 15:06
  • Sorry the S->do_something() is a typo, I've fixed it Commented Feb 8, 2019 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 Commented Feb 8, 2019 at 15:53
  • argtypes should be a list (or tuple): lib.object_pointer.argtypes = [c_void_p]. Commented Feb 8, 2019 at 22:04

1 Answer 1

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.Docs]: ctypes - A foreign function library for Python

object.hpp:

#pragma once #if defined(_WIN32) # if defined(OBJECT_EXPORTS) # define OBJECT_EXPORT_API __declspec(dllexport) # else # define OBJECT_EXPORT_API __declspec(dllimport) # endif #else # define OBJECT_EXPORT_API #endif class Object { public: Object(); virtual ~Object(); virtual double doSomething(); private: double m_double; }; extern "C" { OBJECT_EXPORT_API void* objectNew(); OBJECT_EXPORT_API void objectDel(void *pObj); OBJECT_EXPORT_API double objectDoSomething(void *pObj); } 

object.cpp:

#define OBJECT_EXPORTS #include "object.hpp" #include <iostream> #define DBG_MSG0() std::cout << "CPP - [" << __FILE__ << "] " << __LINE__ << " (" << __FUNCTION__ << ")\n" Object::Object(): m_double(2.718282) { DBG_MSG0(); } Object::~Object() { DBG_MSG0(); } double Object::doSomething() { DBG_MSG0(); return m_double; } extern "C" { void* objectNew() { DBG_MSG0(); return new Object(); } void objectDelete(void *pObj) { DBG_MSG0(); delete reinterpret_cast<Object*>(pObj); } double objectDoSomething(void *pObj) { DBG_MSG0(); if (pObj) { return (reinterpret_cast<Object*>(pObj))->doSomething(); } return 0.0; } } 

code00py:

#!/usr/bin/env python import ctypes as cts import sys DLL_NAME = "./libobject.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so") dll = cts.CDLL(DLL_NAME) objectNew = dll.objectNew objectNew.argtypes = () objectNew.restype = cts.c_void_p objectDelete = dll.objectDelete objectDelete.argtypes = (cts.c_void_p,) objectDoSomething = dll.objectDoSomething objectDoSomething.argtypes = (cts.c_void_p,) objectDoSomething.restype = cts.c_double class ObjectWrapper: def __init__(self): self.obj = objectNew() print("`Object` instance (as a `void *`): 0x{:016X}".format(self.obj)) def __del__(self): print("Deleting instance") objectDelete(self.obj) self.obj = None def do_something(self): print("Doing something") return objectDoSomething(self.obj) def main(*argv): obj = ObjectWrapper() ret = obj.do_something() print("do_something() returned: {:.6f}".format(ret)) if __name__ == "__main__": print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) rc = main(*sys.argv[1:]) print("\nDone.\n") sys.exit(rc) 

Output:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q054594122]> . ~/sopr.sh ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [064bit prompt]> ls code00.py object.cpp object.hpp [064bit prompt]> [064bit prompt]> gcc -shared -fPIC -o libobject.so object.cpp -lstdc++ [064bit prompt]> ls code00.py libobject.so object.cpp object.hpp [064bit prompt]> [064bit prompt]> python ./code00.py Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] 064bit on linux CPP - [object.cpp] 30 (objectNew) CPP - [object.cpp] 11 (Object) `Object` instance (as a `void *`): 0x0000557F47601910 Doing something CPP - [object.cpp] 41 (objectDoSomething) CPP - [object.cpp] 21 (doSomething) do_something() returned: 2.718282 Deleting instance CPP - [object.cpp] 35 (objectDelete) CPP - [object.cpp] 16 (~Object) Done. 

An alternative to manually creating and destroying the (C++) object, is making it static (automatically initialized then the .dll is loaded, and destroyed then the .dll is unloaded).

Might want to look over:

Sign up to request clarification or add additional context in comments.

2 Comments

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...

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.