SWIG:以面向对象的方式包装 C API [英] SWIG: Wrapping C API in OO way

查看:35
本文介绍了SWIG:以面向对象的方式包装 C API的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 C(不是 C++)库,它始终使用函数的第一个参数作为上下文对象(让我们调用类型 t_context),我想使用 SWIG 生成 C# 包装器保持这种调用风格(即,不是将函数或多或少地隔离,而是将它们包装为某个类中的方法,并通过其中的 this 对象的引用访问 t_context方法).

I have a C (not C++) library that consistently uses the first parameter of functions as context object (let's call the type t_context), and I'd like to use SWIG to generate C# wrappers keep this style of call (i.e. instead of the functions being more or less isolated, wrap them as methods in some class and access the t_context via a reference from the this object within the methods).

示例(C 签名):

void my_lib_function(t_context *ctx, int some_param);

所需的 C# API:

Desired 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 生成的包装器,我也会很高兴;我找不到任何东西.

I'd also be happy if someone points out to me a SWIG generated wrapper for an existing C (again: not C++) library that uses this API style; I could not find anything.

或者,除了 SWIG 之外,是否还有用于 C 到 C# 用例的包装器生成器来提供对 API 的更多控制(可能通过公开用于代码生成的模板)?

Alternatively, are there wrapper generators for the C to C# use case other than SWIG that offer more control over the API (perhaps by exposing the templates used for code generation)?

推荐答案

为了解决这个问题,我创建了以下迷你头文件来演示我们(可能)真正关心的所有部分.我这样做的目标是:

In order to work through this problem I've created the following mini header-file to demonstrate all the pieces we (probably) care about to do this for real. My goals in doing this are:

  1. C# 用户甚至不应该意识到这里发生了任何非面向对象的事情.
  2. 如果可能,SWIG 模块的维护者不应该重复所有内容并手动编写大量代理函数.

为了开始工作,我编写了以下头文件 test.h:

To kick things off I wrote the following header file, 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:

And a corresponding test.c with some stub implementations:

#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# 和其他可能的)

There are a few different problems we need to solve to make this into a neat, usable OO C# interface. I'll work through them one at a time and present my preferred solution at the end. (This problem can be solved in a simpler way for Python, but the solution here will be applicable to Python, Java, C# and probably others)

通常在 OO 风格的 C API 中,您会编写某种构造函数和析构函数来封装您的任何设置(可能是不透明的).为了以合理的方式将它们呈现给目标语言,我们可以使用 %extend 来编写看起来很像 C++ 构造函数/析构函数的东西,但在 SWIG 处理之后仍然是 C.

Typically in an OO style C API you'd have some kind of constructor and destructor functions written that encapsulate whatever setup of your (likely opaque). To present them to the target language in a sensible way we can use %extend to write what looks rather like a C++ constructor/destructor, but is still comes out after the SWIG processing as 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);
  }
}

问题二:成员函数

我设置的方式允许我们使用一个可爱的技巧.当我们说:

Problem 2: member functions

The way I've set this up allows us to use a cute trick. When we say:

%extend context {
  void func();
}

然后 SWIG 生成一个看起来像这样的存根:

SWIG then generates a stub that looks like:

SWIGEXPORT void SWIGSTDCALL CSharp_Context_func(void * jarg1) {
  struct context *arg1 = (struct context *) 0 ;

  arg1 = (struct context *)jarg1; 
  context_func(arg1);
}

需要注意的两件事是:

  1. 实现扩展context::func调用的函数称为context_func
  2. 总是有一个隐含的this"等效参数作为参数 1 进入这个函数

上面的内容几乎与我们开始在 C 端包装的内容相匹配.所以要包装它,我们可以简单地做:

The above pretty much matches what we set out to wrap on the C side to begin with. So to wrap it we can simply do:

%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 文件的正确位置.

This doesn't quite meet point #2 of my goals as well as I'd hoped, you have to write out the function declarations manually (unless you use a trick with %include and keeping themin individual header files). With Python you could pull all the pieces together at import time and keep it much simpler but I can't see a neat way to enumerate all the functions that match a pattern into the right place at the point where SWIG generates the .cs files.

这足以让我使用以下代码进行测试(使用 Mono):

This was sufficient for me to test (using Mono) with the following code:

using System;

public class Run
{
    static public void Main()
    {
        Context ctx = new Context();
        ctx.func2("", 0.0);
    }
}

C OO 风格设计的其他变体,使用函数指针 可以解决和类似的问题看 Java 我过去曾讨论过.

There are other variants of C OO style design, using function pointers which are possible to solve and a similar question looking at Java I've addressed in the past.

这篇关于SWIG:以面向对象的方式包装 C API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆