是否可以将 SwigObject 向下转换为具体类型?

问题描述 投票:0回答:1

我有几个 C 文件:

/*mid.h*/
#ifndef mid_h
#define mid_h

#include <stdlib.h> typedef struct PtrRec *Ptr, PtrStruct;

#endif /*mid_h*/ 
/*left.h*/
#ifndef left_h
#define left_h
    
#include "mid.h"

Ptr create_left();
void print_left(Ptr,int);

#endif /*left_h*/


/*left.c*/
#include "left.h"
#include <stdio.h>

struct PtrRec {
    Ptr next;
    int g;
};

void print_left(Ptr left) {
    printf("%zu left{%p}->%p %d\n",sizeof(*l), l, l->next, l->g);
}

Ptr create_left() {
    Ptr rec;
    rec= calloc(1, sizeof(*rec)); 
    rec->g = 33;
    return rec; 
}


/*right.h*/
#ifndef right_h
#define right_h

#include "mid.h"

Ptr create_right();
void print_right(Ptr);

#endif /*right_h*/


/*right.c*/
#include "right.h"
#include <stdio.h>

struct PtrRec {
    int i,j,k;
    Ptr next;
}; 

void print_right(Ptr r) {
    printf("%zu right {%p}->%p %d %d %d\n",sizeof(*r), r, r->next, r->i, r->j, r->k);
}

Ptr create_right() {
    Ptr rec;
    rec= calloc(1, sizeof(*rec)); 
    rec->i = 31;
    rec->j = 34;
    rec->k = 36;
    return rec; 
}

我正在制作一个库并通过 SWIG 导入接口。


%module asd
%{
#include "mid.h"
#include "left.h"
#include "right.h"
%}

%include "mid.h"
%include "left.h"
%include "right.h"
%init %{
    Py_Initialize();
%}

并通过以下方式进行组装:


PYTHON_INCLUDE="/usr/include/python3.11/"

swig -v -python -o asd_wrap.c all.i

clang --shared -I${PYTHON_INCLUDE} -o _asd.so left.c right.c asd_wrap.c

所以我可以从Python导入它并调用一些函数:

from asd import *

left = create_left()
right = create_right()
print_left(left)
print_right(right)

那么,有没有办法扩展这样的代码,使

PtrRec
与 python 端区分开来?任何向下转换它并附加额外方法的方法,都可以从 python 访问,比如附加一些
get_size
方法,这样它们至少可以打印它们的大小。这样的设置是否可行,或者实际上需要使用不同的名称来创建它们的结构并显式地将它们放入头文件中?

python c pointers polymorphism swig
1个回答
0
投票

简而言之 - 不,你不能按原样投射它。然而,可以为指针创建通用接口。 SWIG 可见的所有内容都应放入头文件中,因此我们可以使用公共字段和一些类型标记来对结构进行存根实现,以允许推断如何处理它们。

/*swig_api.h*/

#ifndef api_h
#define api_h

typedef enum {
    LEFT,
    RIGHT,
} ptr_type;

struct PtrRec {
    Ptr next;
    ptr_type type;
};

void change_ptr(Ptr ptr);

#endif

/*api.c*/
#include "left.h"
#include "right.h"
#include "api.h"

void change_ptr(Ptr ptr) {
    if (!ptr) return;
    /*at this scope only stub fields are visible
    * but will be autocasted inside concrete calls
    */
    switch (ptr->type) {
        case LEFT: change_left(ptr); break;
        case RIGHT: change_right(ptr); break;
        default:
            break;
    }
}

并且显然将它们添加到 swig 的代码生成中

%module asd
%{
...
#include "api.h"
...
%}

...
%include "api.h"
...

为了防止 python 代码中所有公共字段出现任何段错误和异常,我们订购与存根相同的具体实现并添加类型标记。 所以,固定版本看起来像这样

/*left.c*/
...

struct PtrRec {
    Ptr next;
    ptr_type type;
    int g;
};

Ptr create_left() {
    Ptr rec;
    rec= calloc(1, sizeof(*rec)); 
    rec->type = LEFT;  /* don't forget type tag*/
    rec->g = 33;
    return rec; 
}
void change_left(Ptr ptr) {
    if (!ptr) return;
    ptr->g = 1387;
}

/*right.c*/
...
struct PtrRec {
    Ptr next;
    ptr_type type;
    int i,j,k;
};

Ptr create_right() {
    Ptr rec;
    rec= calloc(1, sizeof(*rec)); 
    rec->type = RIGHT; /* don't forget type tag*/
    rec->i = 31;
    rec->j = 34;
    rec->k = 36;
    return rec; 
}

void change_right(Ptr ptr) {
    if (!ptr) return;
    ptr->i = 61947;
}

所以,从Python我们终于可以看到必要的字段了:

from asd import *
left = create_left();
right = create_right();

change_ptr(left);  # internally will change left.g to 1387
change_ptr(right); # internally will change right.i to 61947

使用这个技巧,我们可以进一步扩展 Python 的 API,同时具有作用域多态性。 重要提示:

  • 字段始终应遵循与 API 存根相同的顺序,否则 c 模块将出现段错误并将异常抛出到 python 中。
  • 尝试将一种类型放入函数中,需要其他类型也会以段错误结束。尽管在 C 中,它只会解释存在的任何内存并对其进行写入,直到没有结构边界跨越。
  • 每个结构实例化都需要调用自己正确的构造函数 - 在本例中为
    create_left
    create_right
    - 而不是通过
    StructureName()
    直接构造,因为它将仅构造公共 API 的公共部分,而不是其他不可见的数据Python。将这样的结构提供给指定的函数也会由于结构边界交叉而导致段错误。
© www.soinside.com 2019 - 2024. All rights reserved.