我有一个C(不是C ++)库,它始终使用函数的第一个参数作为上下文对象(让我们调用类型t_context
),我想使用SWIG来生成C#包装器保持这种调用方式(即代替函数或多或少孤立,将它们包装为某些类中的方法,并通过方法中t_context
对象的引用访问this
示例(C签名):
void my_lib_function(t_context *ctx, int some_param);
所需的C#API:
class Context
{
// SWIG generated struct reference
private SWIG_t_context_ptr ctx;
public void my_lib_function(int some_param)
{
// call SWIG generated my_lib_function with ctx
}
}
如果有人向我指出使用此API样式的现有C(再次:非C ++)库的SWIG生成包装器,我也很高兴;我找不到任何东西。
或者,是否存在除SWIG以外的C to C#用例的包装器生成器,它可以提供对API的更多控制(可能通过公开用于代码生成的模板)?
答案 0 :(得分:1)
为了解决这个问题,我创建了以下迷你标题文件来演示我们(可能)关心真正做到这一点的所有部分。我这样做的目标是:
为了解决问题,我编写了以下头文件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更简单的方式解决,但这里的解决方案将适用于Python,Java,C#和其他人)
通常在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);
}
}
我设置它的方式允许我们使用一个可爱的技巧。当我们说:
%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
以上几乎与我们开始在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文件的正确位置。
这足以让我使用以下代码测试(使用Mono):
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。