Wie verwende ich malloc und kostenlos mit Python-Ctypes?

Lesezeit: 4 Minuten

Benutzeravatar von Curious2learn
Neugierig2lernen

Ich habe eine Funktion in meiner C-Bibliothek, sagen wir runsim() was einen Zeiger auf nimmt struct repdata als eines der Argumente, wo struct repdata wird von gegeben

struct repdata {
    int *var1;
    int *var2;
    int *var3;
    char *var4;
    double *var5;
    double *var6;
    int *var7;
};

Wenn ich ausschließlich C verwende, initialisiere ich eine Variable vom Typ struct repdata Aufruf der Funktion,

struct repdata data;
void create_data_container(struct repdata *data, int len_data)
{

    data -> var1 = malloc( sizeof(int) * len_data );
    data -> var2 = malloc( sizeof(int) * len_data );
    data -> var3 = malloc( sizeof(int) * len_data );
    data -> var4 = malloc( sizeof(char) * len_data );
    data -> var5 = malloc( sizeof(double) * len_data);
    data -> var6 = malloc( sizeof(double) * len_data);
    data -> var7 = malloc( sizeof(int) * len_data);
}

und dann diese Struktur füllen, während die Simulation fortschreitet. Nachdem ich die Daten in eine Datei geschrieben habe, gebe ich den Speicher per Standard frei

free(data.var1);
free(data.var2);
.
.
.
free(data.var7);

Ich möchte die anrufen runsim() Funktion aus Python mit Python Ctypes. Dazu muss ich einen Zeiger auf eine Variable übergeben (das entspricht dem Typ von struct repdata) als einer der runsim() Argumente. Angenommen, ich habe in Python ein Äquivalent von definiert struct repdata auf folgende Art.

import ctypes as C

class Repdata(C.Structure):
    _fields_ = [
        ("var1", C.POINTER(C.c_int)),
        ("var2", C.POINTER(C.c_int)),
        ("var3", C.POINTER(C.c_int)),
        ("var4", C.POINTER(C.c_char)),
        ("var5", C.POINTER(C.c_double)),
        ("var6", C.POINTER(C.c_double)),
        ("var7", C.POINTER(C.c_int)),
    ]

Was ist das Äquivalent von create_data_container oben gezeigte Funktion auf der Python-Seite? Ich möchte eine Instanz von Repdata initialisieren, die an C-Code übergeben werden kann und über ausreichend Speicher zum Speichern der Replikationsdaten verfügt. Und wie gebe ich nach Abschluss der Simulation den Speicher von Python frei?

Ich verwende Ubuntu Linux 12.04.

Vielen Dank im Voraus für Ihre Hilfe.

Benutzeravatar von Mark Tolonen
Markus Tolonen

Sie können Puffer mit zuweisen ctypes und weisen sie den Zeigern zu. Sobald die Python-ctypes-Objekte keine Referenzen mehr haben, werden sie automatisch freigegeben. Hier ist ein einfaches Beispiel (mit einer Windows-DLL … ich habe keinen Linux-Computer zur Hand, aber die Idee ist dieselbe) und ein Python-Wrapper.

create_string_buffer weist einen beschreibbaren Puffer zu, der von Python an C übergeben werden kann ctypes wird als ein marschieren char*.

Sie können auch beschreibbare Arrays von erstellen ctypes Typen mit der Syntax:

variable_name = (ctypes_type * length)(initial_values)

xh

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

struct example {
    char* data;
    int len;          // of data buffer
    double* doubles;
    int count;        // of doubles
};

DLL_API void func(struct example* p);

xc

#include <stdio.h>
#define DLL_EXPORTS
#include "x.h"

void func(struct example* p)
{
    int i;
    strcpy_s(p->data,p->len,"hello, world!");
    for(i = 0; i < p->count; i++)
        p->doubles[i] = 1.1 * (i + 1);
}

x.py

import ctypes

class Example(ctypes.Structure):

    _fields_ = [
        ('data',ctypes.POINTER(ctypes.c_char)),
        ('len',ctypes.c_int),
        ('doubles',ctypes.POINTER(ctypes.c_double)),
        ('count',ctypes.c_int)]

    def __init__(self,length,count):
        self.data = ctypes.cast(ctypes.create_string_buffer(length),ctypes.POINTER(ctypes.c_char))
        self.len = length
        self.doubles = (ctypes.c_double * count)()
        self.count = count

    def __repr__(self):
        return 'Example({},[{}])'.format(
            ctypes.string_at(self.data),
            ','.join(str(self.doubles[i]) for i in range(self.count)))

class Dll:

    def __init__(self):
        self.dll = ctypes.CDLL('x')
        self.dll.func.argtypes = [ctypes.POINTER(Example)]
        self.dll.func.restype = None

    def func(self,ex):
        self.dll.func(ctypes.byref(ex))

d = Dll()
e = Example(20,5)
print('before:',e)
d.func(e)
print ('after:',e)

Ausgabe

before: Example(b'',[0.0,0.0,0.0,0.0,0.0])
after: Example(b'hello, world!',[1.1,2.2,3.3000000000000003,4.4,5.5])

  • Ich kann mich irren, aber das scheint anders zu sein als das, wonach ich suche. In meinem Problem werden die Daten im C-Code generiert und ich möchte einen Container an den C-Code von Python übergeben, der mit diesen Daten gefüllt werden kann. In der Antwort hier werden die Daten von Python an C übergeben.

    – Neugierig2lernen

    8. September 2013 um 3:42 Uhr


  • @Curious2learn, Die Puffer werden zugewiesen, müssen aber nicht auf der Python-Seite initialisiert werden. Sie können sie auch auf der C-Seite ausfüllen und die Daten erscheinen auf der Python-Seite. Ich werde das Obige ändern, um dies stattdessen zu demonstrieren.

    – Mark Tolonen

    8. September 2013 um 5:11 Uhr

  • @MarkTolonen Auf deine Antwort stoßen: könnte ich “injizieren” create_string_buffer Als ein malloc Ersatz in meine C-Bibliothek via CFUNCTYPE(c_void_p, c_size_t)(create_string_buffer)? Ich möchte vermeiden, meine C-Bibliothek zu “fragen”, wie viel Speicher sie die ganze Zeit benötigt …

    – Sebastian

    14. November 2014 um 15:20 Uhr


1432360cookie-checkWie verwende ich malloc und kostenlos mit Python-Ctypes?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy