爲了通過這個問題,我創建了以下小型頭文件來證明一切,我們(可能)關心,爲真正做到這件工作。我這樣做的目標是:
- 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