为了通过这个问题,我创建了以下小型头文件来证明一切,我们(可能)关心,为真正做到这件工作。我这样做的目标是:
- C#用户甚至不应该意识到这里有什么非OO发生。
- 你痛饮模块的维护者应该不会有回音的一切,如果可以通过手工编写大量的代理功能。
要踢东西了,我写了下面的头文件,test.h:
#ifndef TEST_H
#define TEST_H
struct context;
typedef struct context context_t;
void init_context(context_t **new);
void fini_context(context_t *new);
void context_func1(context_t *ctx, int arg1);
void context_func2(context_t *ctx, const char *arg1, double arg2);
#endif
和相应的test.c的一些存根实现:
#include <stdlib.h>
#include "test.h"
struct context {};
typedef struct context context_t;
void init_context(context_t **new) {
*new = malloc(sizeof **new);
}
void fini_context(context_t *new) {
free(new);
}
void context_func1(context_t *ctx, int arg1) {
(void)ctx;
(void)arg1;
}
void context_func2(context_t *ctx, const char *arg1, double arg2) {
(void)ctx;
(void)arg1;
(void)arg2;
}
有几个我们需要解决不同的问题,以使其成为一个整洁,可用的OO C#界面。我会一次一个地解决它们,并最终提出我的首选解决方案。 (这个问题可以用简单的方法解决,但这里的解决方案适用于Python,Java,C#和其他可能的解决方案)
问题1:构造函数和析构函数。
通常在OO风格的C API中,你会有某种类型的构造函数和析构函数,它们封装了你的任何设置(可能是不透明的)。将它们呈现在我们可以使用%extend
写什么看起来有点像一个C++构造函数/析构函数一个明智的方式目标语言,但SWIG加工为C.
%module test
%{
#include "test.h"
%}
%rename(Context) context; // Make it more C# like
%nodefaultctor context; // Suppress behaviour that doesn't work for opaque types
%nodefaultdtor context;
struct context {}; // context is opaque, so we need to add this to make SWIG play
%extend context {
context() {
context_t *tmp;
init_context(&tmp);
// we return context_t * from our "constructor", which becomes $self
return tmp;
}
~context() {
// $self is the current object
fini_context($self);
}
}
问题2之后仍然出来:成员功能
我已经设置这允许我们使用一个可爱的把戏的方式。当我们说:
%extend context {
void func();
}
SWIG然后生成一个存根,看起来像:
SWIGEXPORT void SWIGSTDCALL CSharp_Context_func(void * jarg1) {
struct context *arg1 = (struct context *) 0 ;
arg1 = (struct context *)jarg1;
context_func(arg1);
}
两件事情要远离那些:
- 实现扩展
context::func
功能呼叫被称为context_func
- 有一个隐含的“this”等价参数作为argum进入该函数ent 1总是
上面的内容非常符合我们在C端打包的内容。所以把它包起来,我们可以简单地做:
%module test
%{
#include "test.h"
%}
%rename(Context) context;
%nodefaultctor context;
%nodefaultdtor context;
struct context {};
%extend context {
context() {
context_t *tmp;
init_context(&tmp);
return tmp;
}
~context() {
fini_context($self);
}
void func1(int arg1);
void func2(const char *arg1, double arg2);
}
这并不完全符合点#我的目标2以及我所希望的,你必须手动写出来的函数声明(除非您使用诀窍%include
并保留最小个别头文件)。使用Python,您可以在导入时将所有的部分组合在一起,并使其更加简单,但我无法看到一种简洁的方式来枚举与SWIG生成.cs文件的位置相匹配的所有功能的正确位置。
这足以让我用下面的代码测试(使用单声道):
using System;
public class Run
{
static public void Main()
{
Context ctx = new Context();
ctx.func2("", 0.0);
}
}
有other variants of C OO style design, using function pointers这是可能的解决和类似的问题looking at Java我已经在过去处理。
如果您想从许多C函数创建一个类,您需要以某种方式告诉接口生成器,哪些函数属于给定的类。我猜最简单的方法是使用匿名结构“typedef struct _Context Context;'并使用'%extend'directive来添加调用封装C函数的成员函数。你将不得不在你的'.i'文件中为每个函数添加3行,就像你将C包装到C++类中一样。通过这种方式,您可以避免更改原始标题并且不需要重新编译您的C库 –
是否有任何命名约定可以让您推断它们属于哪个“类”?如果这是一个选项,你可以自动化更多你想要的东西。 – Flexo
是的,我可以控制函数的命名,所以很容易以my_lib_api_ *的形式命名应以这种方式公开的所有函数。 – pmf