包裹C库

2 投票
2 回答
2330 浏览
提问于 2025-04-17 03:29

我有一个 private.h 文件,一个 public.h 文件,还有一个 file.c 文件,我需要把它们包装成 Cython 格式。请问我该如何包装这个函数 Person_ptr Person_create(const char* name); 呢?

private.h:

#ifndef __PERSON_PRIVATE_H__
#define __PERSON_PRIVATE_H__

#include "Person.h"

typedef struct Person_TAG {
    char* name;
    int age;
} Person;


void person_init(Person_ptr self, const char* name);

# endif /* __PERSON_PRIVATE_H__ */

public.h

#ifndef __PERSON_H__
#define __PERSON_H__

#include <assert.h>

typedef struct Person_TAG* Person_ptr;

#define PERSON(x) \
    ((Person_ptr) (x))

#define PERSON_CHECK_INSTANCE(x) \
    assert(PERSON(x) != PERSON(NULL))

/* public interface */
Person_ptr Person_create(const char* name);
void Person_destroy(Person_ptr self);

const char* Person_get_name(const Person_ptr self);
int Person_get_age(const Person_ptr self);
void Person_birthday(Person_ptr self);

# endif /* __PERSON_H__ */

这是 file.c 的内容:

#include "Person.h"
#include "Person_private.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

Person_ptr Person_create(const char* name)
{
    Person_ptr self = PERSON(malloc(sizeof(Person)));
    PERSON_CHECK_INSTANCE(self);
    person_init(self, name);
    return self;
}

void Person_destroy(Person_ptr self)
{
    PERSON_CHECK_INSTANCE(self);
    if (NULL != self->name) free(self->name);
}

 /* Do not free not change the returned string! */
const char* Person_get_name(const Person_ptr self)
{
    PERSON_CHECK_INSTANCE(self);
    return self->name;
}

int Person_get_age(const Person_ptr self)
{
    PERSON_CHECK_INSTANCE(self);
    return self->age;
}

void Person_birthday(Person_ptr self)
{
    PERSON_CHECK_INSTANCE(self);
    ++self->age;
}

/* private/protected methods */
void person_init(Person_ptr self, const char* name)
{
    self->name = malloc(sizeof(char) * (strlen(name) + 1));
    assert(NULL != self->name);
    strcpy(self->name, name);
    self->age = 0;
}

我尝试做的是 file.pyx

from ctypes import *

cdef extern from "Person.h":
ctypedef struct Person_ptr:
        pass
Person_ptr Person_create "P_create" (char *name)

cdef class Person:
    cdef Person P_create(p_name):
    return Person_create(p_name)

这样做不对——它在 Cython 中可以编译,但在 GCC 中却出现了很多错误。我该如何解决这个问题呢?

2 个回答

1

一般来说,手动做这种事情没有太大意义,除非你有一些特别的需求。相反,你可以使用SWIG来为你生成这些包装代码。你不需要费力去搞清楚这些细节,而且当C代码发生变化时,重新生成新的包装代码也非常简单。

7

cperson.pxd:

cdef extern from "Person.h":
    cdef struct Person_TAG:
        pass
    ctypedef Person_TAG* Person_ptr

    Person_ptr Person_create(char* name)
    void Person_destroy(Person_ptr self)

    char* Person_get_name(Person_ptr self)

person.pyx:

from cperson cimport Person_ptr, Person_create, Person_destroy, Person_get_name

cdef class Person:
    cdef Person_ptr thisptr

    def __cinit__(self, char* name):
        self.thisptr = Person_create(name)
        if self.thisptr is NULL:
            raise MemoryError

    def __dealloc__(self):
        if self.thisptr is not NULL:
            Person_destroy(self.thisptr)

    property name:
        def __get__(self):
            return Person_get_name(self.thisptr)

person.pyxbld:

import os
from distutils.extension import Extension

dirname = os.path.dirname(__file__)

def make_ext(modname, pyxfilename):
    return Extension(name=modname,
        sources=[pyxfilename, "Person.c"],
        include_dirs=[dirname],
        # or libraries=["person"], if you compile Person.c separately 
        )

test_person.py:

import pyximport; pyximport.install()

from person import Person

p = Person('Garry')
print p.name

示例

$ python test_person.py 
Garry

撰写回答