如何使用 SWIG 从 C 调用 C# 方法?
How to call a C# method from C using SWIG?
我一直在搜索一些 explanations/code 片段,这些片段说明了如何使用 SWIG 从 C 调用 C# 方法。我能找到的最好的是:Callback from C++ to C# using SWIG。 las,这是针对 C++ 而不是 C。如果不是 C,我们如何才能实现相同的功能?
您仍然可以使用 SWIG 来帮助(一点点)做到这一点,但从根本上说,如果您使用的是 C 编译器,则无法启用 SWIG 导向器,您链接到的示例依赖于:
test.i:1: Error: Directors are not supported for C code and require the -c++ option
因此您必须在 C 中重新构建该功能的某些部分才能执行此操作。我构建了一个启用了 C++ 的 director/callbacks 设置示例,并将其用作构建我对 C# 机制的理解的基础。让我们从后面开始,从我们想要的测试开始 运行:
public class TestImpl : CallbackInterface {
public override void callback() {
System.Console.WriteLine("handling event...");
}
public static void Main() {
CallbackInterface cb = new TestImpl();
test.test_runner(cb);
}
}
我们想要定义一个抽象 class,CallbackInterface
我们可以传递实例并让我们的 C 代码调用 C# callback
实现。
这是我的 .i SWIG 接口文件:
%module test
%{
#include <assert.h>
%}
%typemap(csclassmodifiers) struct CallbackInterface "public abstract class";
%typemap(cscode) struct CallbackInterface %{
public delegate void callback0(); // [2]
public CallbackInterface() : this(null) { // [3]
connect();
}
private void connect() {
setCallback(new callback0(callback));
}
public abstract void callback();
%}
// [4]
%typemap(cstype) cb0 "CallbackInterface.callback0";
%typemap(imtype) cb0 "CallbackInterface.callback0";
%typemap(csin) cb0 "$csinput";
%ignore CallbackInterface::cb;
%extend struct CallbackInterface {
CallbackInterface(cb0 cb) { // [3]
struct CallbackInterface *ret = malloc(sizeof *ret);
ret->cb = cb;
return ret;
}
void setCallback(cb0 cb) {
$self->cb = cb;
}
}
%inline %{
typedef void (*cb0)();
struct CallbackInterface {
cb0 cb; // [1]
};
static void test_runner(struct CallbackInterface *interface) {
assert(interface->cb);
fprintf(stderr, "Doing test, pointer=%p, cb=%p\n", interface, interface->cb);
interface->cb();
}
%}
通过调查 C# 中的正常 director 实现,C++ 层似乎正在通过 pinvoke 接口使用函数指针。所以我们在 1 处的接口中定义了一个结构体,它有一个成员,一个函数指针。
为了从我们的 C# 代码中实际获取函数指针,我们需要使用委托,我们已经为此在 2 处进行了设置,这是将成为我们 [=14 的一部分的 C# 代码=] class.
在 SWIG 中,我们“扩展”(但仍仅使用 C)我们的结构以提供构造函数,该构造函数在 3 处将委托作为参数。这有点奇怪,因为我们只传递 null
在这里,但这可以作为我们默认构造函数的方便重载,并确保我们在创建实例时至少也初始化结构的 cb
成员,并在 3 处调用委托构造函数.(旁白:我最初尝试在此处创建委托,但这似乎是一个静态上下文,因此此时您不能这样做)。然后在默认构造函数的主体中,我们调用我们的私有 connect
方法来设置委托并使用我们的 %extend
setCallback
方法实际修改结构。
如果您愿意,您也可以提供 setCallback
的重载,以便可以将 C 函数指针设置为处理程序,但这是一个额外的额外功能,特别是考虑到 C 限制意味着您可以'根本不写 classes。在 4 处使用一些额外的类型映射来确保委托作为函数指针传递给 C,因此下面的 Makefile 可以 运行 并为我们进行测试:
.PHONY: run
all: run
test_wrap.c: test.i
swig -csharp -Wall -debug-tmsearch test.i
test.cs: test_wrap.c
CallbackInterface.cs: test_wrap.c
testPINVOKE.cs: test_wrap.c
test.exe: test.cs runme.cs CallbackInterface.cs testPINVOKE.cs
mcs $^
libtest.so: test_wrap.c
gcc -shared -Wall -Wextra $^ -o $@ -fPIC
run: test.exe libtest.so
LD_LIBRARY_PATH=. ./test.exe
在 Ubuntu 上使用 Mono 至少可以按预期工作。
我一直在搜索一些 explanations/code 片段,这些片段说明了如何使用 SWIG 从 C 调用 C# 方法。我能找到的最好的是:Callback from C++ to C# using SWIG。 las,这是针对 C++ 而不是 C。如果不是 C,我们如何才能实现相同的功能?
您仍然可以使用 SWIG 来帮助(一点点)做到这一点,但从根本上说,如果您使用的是 C 编译器,则无法启用 SWIG 导向器,您链接到的示例依赖于:
test.i:1: Error: Directors are not supported for C code and require the -c++ option
因此您必须在 C 中重新构建该功能的某些部分才能执行此操作。我构建了一个启用了 C++ 的 director/callbacks 设置示例,并将其用作构建我对 C# 机制的理解的基础。让我们从后面开始,从我们想要的测试开始 运行:
public class TestImpl : CallbackInterface {
public override void callback() {
System.Console.WriteLine("handling event...");
}
public static void Main() {
CallbackInterface cb = new TestImpl();
test.test_runner(cb);
}
}
我们想要定义一个抽象 class,CallbackInterface
我们可以传递实例并让我们的 C 代码调用 C# callback
实现。
这是我的 .i SWIG 接口文件:
%module test
%{
#include <assert.h>
%}
%typemap(csclassmodifiers) struct CallbackInterface "public abstract class";
%typemap(cscode) struct CallbackInterface %{
public delegate void callback0(); // [2]
public CallbackInterface() : this(null) { // [3]
connect();
}
private void connect() {
setCallback(new callback0(callback));
}
public abstract void callback();
%}
// [4]
%typemap(cstype) cb0 "CallbackInterface.callback0";
%typemap(imtype) cb0 "CallbackInterface.callback0";
%typemap(csin) cb0 "$csinput";
%ignore CallbackInterface::cb;
%extend struct CallbackInterface {
CallbackInterface(cb0 cb) { // [3]
struct CallbackInterface *ret = malloc(sizeof *ret);
ret->cb = cb;
return ret;
}
void setCallback(cb0 cb) {
$self->cb = cb;
}
}
%inline %{
typedef void (*cb0)();
struct CallbackInterface {
cb0 cb; // [1]
};
static void test_runner(struct CallbackInterface *interface) {
assert(interface->cb);
fprintf(stderr, "Doing test, pointer=%p, cb=%p\n", interface, interface->cb);
interface->cb();
}
%}
通过调查 C# 中的正常 director 实现,C++ 层似乎正在通过 pinvoke 接口使用函数指针。所以我们在 1 处的接口中定义了一个结构体,它有一个成员,一个函数指针。
为了从我们的 C# 代码中实际获取函数指针,我们需要使用委托,我们已经为此在 2 处进行了设置,这是将成为我们 [=14 的一部分的 C# 代码=] class.
在 SWIG 中,我们“扩展”(但仍仅使用 C)我们的结构以提供构造函数,该构造函数在 3 处将委托作为参数。这有点奇怪,因为我们只传递 null
在这里,但这可以作为我们默认构造函数的方便重载,并确保我们在创建实例时至少也初始化结构的 cb
成员,并在 3 处调用委托构造函数.(旁白:我最初尝试在此处创建委托,但这似乎是一个静态上下文,因此此时您不能这样做)。然后在默认构造函数的主体中,我们调用我们的私有 connect
方法来设置委托并使用我们的 %extend
setCallback
方法实际修改结构。
如果您愿意,您也可以提供 setCallback
的重载,以便可以将 C 函数指针设置为处理程序,但这是一个额外的额外功能,特别是考虑到 C 限制意味着您可以'根本不写 classes。在 4 处使用一些额外的类型映射来确保委托作为函数指针传递给 C,因此下面的 Makefile 可以 运行 并为我们进行测试:
.PHONY: run
all: run
test_wrap.c: test.i
swig -csharp -Wall -debug-tmsearch test.i
test.cs: test_wrap.c
CallbackInterface.cs: test_wrap.c
testPINVOKE.cs: test_wrap.c
test.exe: test.cs runme.cs CallbackInterface.cs testPINVOKE.cs
mcs $^
libtest.so: test_wrap.c
gcc -shared -Wall -Wextra $^ -o $@ -fPIC
run: test.exe libtest.so
LD_LIBRARY_PATH=. ./test.exe
在 Ubuntu 上使用 Mono 至少可以按预期工作。