2017-07-23 22 views
1

Ich bin sehr neu zu Ctypes, und ich bekomme einen Fehler mit dem einfachen Programm unten.Warum bekomme ich hier einen Segmentierungsfehler? [Python ctypes]

foo.cpp

class Foo { 
public: 
    int bar; 
    Foo(int bar): bar(bar) {} 
}; 

extern "C" { 
    Foo * Foo_new(int bar) { 
     return new Foo(bar); 
    } 

    int Foo_bar(Foo *foo) { 
     return foo->bar; 
    } 
} 

foo.py

import ctypes 

libfoo = ctypes.CDLL('libfoo.so') 


class Foo: 
    def __init__(self, bar): 
     self.foo = libfoo.Foo_new(bar) 

    def bar(self): 
     return libfoo.Foo_bar(self.foo) 


foo = Foo(5) 
print(foo.bar()) 

Der Segmentierungsfehler passiert, wenn ich libfoo.Foo_bar(self.foo) nennen, aber nach this answer, dachte ich alles, was ich brauchte, war zu tun, eine starke Referenz machen über self.foo, so dass es nicht Müll gesammelt wurde.

Meine Vermutung ist, dass Foo auf dem Stapel in der CPP-Datei gemacht wird, so dass es sofort nach dem Funktionsaufruf gelöscht wird. In jedem Fall, wie repariere ich das?

+0

ctypes hat keine Ahnung, welche Funktionen diese Funktionen übernehmen oder zurückgeben. Zum Beispiel hat es keine Ahnung, was ein 'Foo *' ist und keine Ahnung, dass 'Foo_new' einen zurückgibt. – user2357112

+0

Garbage Collection kommt für Ctypes-Callbacks ins Spiel, nicht für einen Zeiger auf ein Objekt, das nichts direkt mit Python zu tun hat. Es wird hier segzufällig, weil die Standard-Integer-Ergebnistyp- und -argumentkonvertierung ein 32-Bit-C-int-Wert ist, der einen 64-Bit-Zeiger abschneidet. – eryksun

Antwort

3

Sie müssen die Argumente und Rückgabetypen explizit umbrechen, sonst wird ctypes einige willkürliche Standardwerte übernehmen, die funktionieren könnten oder nicht. Um den Zeiger auf die Klasse Foo zu wickeln, würde ich einen Zeiger auf void c_void_p verwenden. Ich bin mir nicht sicher, ob das der richtige Ansatz ist, aber es scheint zu funktionieren.

import ctypes 

libfoo = ctypes.CDLL('libfoo.so') 

libfoo.Foo_new.argtypes = [ctypes.c_int] 
libfoo.Foo_new.restype = ctypes.c_void_p 

libfoo.Foo_bar.argtypes = [ctypes.c_void_p] 
libfoo.Foo_bar.restype = ctypes.c_int 

class Foo: 
    def __init__(self, bar): 
     self.foo = libfoo.Foo_new(bar) 

    def bar(self): 
     return libfoo.Foo_bar(self.foo) 

foo = Foo(5) 
print(foo.bar()) 

I nehmen in der Regel einen anderen Weg C++ Code mit Python Schnittstelle, die durch eine Erweiterung Python SWIG Verwendung zu erzeugen. Deshalb würde ich in eine Datei schreiben test.h

class Foo { 
public: 
    int bar; 
    Foo(int bar): bar(bar) {} 
}; 

Dann würde ich test.i

%module test 
%{ 
#include "test.h" 
%} 
%include "test.h" 

Dann eine SWIG Interface-Datei schreibe ich im Terminal ausführen

$ swig -python -c++ test.i 
$ c++ -fpic -shared -I/usr/include/python2.7 test_wrap.cxx -o _test.so 

Und dann kann ich schreibe in ein Python-Skript

from test import Foo 
foo = Foo(5) 
print foo.bar 
+0

'c_void_p' ist im Allgemeinen nicht sehr typsicher, verglichen mit einem opaken Strukturzeiger. In diesem Fall spielt es jedoch keine Rolle, da die "foo" -Instanz gekapselte Daten sind. – eryksun

Verwandte Themen