swig可以自动生成从c++到其他语言如Java、Python等转换的中间语言,目前swig已经支持很多c++11的特性了,但是这次项目中发现function特性还没有支持,只能自己生成。
从网上找了一份Java的java - How to use SWIG to wrap std::function objects? - Stack Overflow,我需要的c#的,故需要稍微修改一下。
主要思想是为std::function生成结构体,这样结构体的operator()函数就可以映射到c#中作为回调函数,要注意的是operator()函数要是虚函数,c#那边才能重写。
其他部分还有:1.利用宏和__VA_ARGS__实现自定义多个参数;2.使用抽象类和代理类避免c#function自动gc。
至于用法就很简单了,比如%define %std_function(my_fuc, int, int),c#那边就继承my_fuc类,并重写my_fucDelegate函数,然后将对象传给c++即可。
而我的项目中并不止调用fuc,还要存储并可能查找和删除,但是如果使用对象的话,每次从c#传到c++时在swig层会重新new一个c++对象,导致无法根据对象查找到之前的,因此最后选择的是存储对象指针,c++和c#共用一份指针,由c#生成和自动释放。
总结一下这次使用swig的感谢:因为之前是使用c++/cli从c++转为c#,虽然需要手写,比较麻烦,但好处是完全可以自定义写法,一些困难之处可以通过独特的方法解决;这次使用swig是自动生成中间语言,虽然是自动,但过程中遇到很多困难,因为需要根据swig规则编写.i文件,就需要详细了解swig规则,并且有一些特性或用法swig目前尚未支持(特别是设计模板和宏的地方,很容易踩坑)。为了解决这些困难,耗时最短的竟然是在swig生成的中间语言文件中做自定义编写,也就是改动自动生成的代码,但是这样做会使后面的生成更加麻烦,代码会十分混乱(并且swig生成的文件会十分庞大)。
针对swig,坚决不能手动改动自动生成的文件,但是可以在.i文件中做一些处理,使swig自己调整代码。还有一点就是写脚本,在swig生成后使用脚本批量改动自动生成的文件,这样会方便很多。
1 %{ 2 #include <functional> 3 #include <iostream> 4 5 #ifndef SWIG_DIRECTORS 6 #error "Directors must be enabled in your SWIG module for std_function.i to work correctly" 7 #endif 8 %} 9 10 // These are the things we actually use 11 #define param(num,type) $typemap(cstype,type) arg ## num 12 #define unpack(num,type) arg##num 13 #define lvalref(num,type) type arg##num 14 #define forward(num,type) std::forward<type>(arg##num) 15 16 // This is the mechanics 17 #define FE_0(...) 18 #define FE_1(action,a1) action(0,a1) 19 #define FE_2(action,a1,a2) action(0,a1), action(1,a2) 20 #define FE_3(action,a1,a2,a3) action(0,a1), action(1,a2), action(2,a3) 21 #define FE_4(action,a1,a2,a3,a4) action(0,a1), action(1,a2), action(2,a3), action(3,a4) 22 #define FE_5(action,a1,a2,a3,a4,a5) action(0,a1), action(1,a2), action(2,a3), action(3,a4), action(4,a5) 23 24 #define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME 25 #define tostring(x) #x 26 27 %define FOR_EACH(action,...) 28 GET_MACRO(__VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0)(action,__VA_ARGS__) 29 %enddef 30 31 32 %define %std_function(Name, Ret, ...) 33 34 %feature("director") Name##Impl; 35 %typemap(csclassmodifiers) Name##Impl "public abstract class"; 36 37 %{ 38 struct Name##Impl { 39 virtual ~Name##Impl() {} 40 virtual Ret Name##Delegate(__VA_ARGS__) = 0; 41 }; 42 %} 43 44 %csmethodmodifiers Name##Impl::Name##Delegate "abstract protected"; 45 %typemap(csout) Ret Name##Impl::Name##Delegate ";" // Suppress the body of the abstract method 46 47 struct Name##Impl { 48 virtual ~Name##Impl(); 49 protected: 50 virtual Ret Name##Delegate(__VA_ARGS__) = 0; 51 }; 52 53 %typemap(maybereturn) SWIGTYPE "return "; 54 %typemap(maybereturn) void ""; 55 56 %typemap(csin) std::function<Ret(__VA_ARGS__)>& "$csclassname.getCPtr($csclassname.makeNative($csinput))" 57 %typemap(csin) std::function<Ret(__VA_ARGS__)> "$csclassname.getCPtr($csclassname.makeNative($csinput))" 58 %typemap(csin) std::function<Ret(__VA_ARGS__)> * "$csclassname.getCPtr($csclassname.makeNative($csinput))" 59 60 %typemap(cscode) std::function<Ret(__VA_ARGS__)> %{ 61 public class Name##Impl##Cb: Name##Impl 62 { 63 protected override $typemap(cstype, Ret) Name##Delegate(FOR_EACH(param, __VA_ARGS__)){ 64 $typemap(maybereturn, Ret)m_cb.Name##Delegate(FOR_EACH(unpack, __VA_ARGS__)); 65 } 66 67 public void Set##Name(Name cb){ 68 m_cb = cb; 69 } 70 71 private Name m_cb; 72 } 73 74 protected Name() { 75 wrapper = new Name##Impl##Cb(); 76 Name##Impl##Cb tmpwrapper = wrapper as Name##Impl##Cb; 77 tmpwrapper.Set##Name(this); 78 proxy = new $csclassname(wrapper); 79 } 80 81 static public $csclassname makeNative($csclassname varin) { 82 if (null == varin.wrapper) return varin; 83 return varin.proxy; 84 } 85 86 // Bot of these are retained to prevent garbage collection from happenign to early 87 private Name##Impl wrapper; 88 private $csclassname proxy; 89 %} 90 91 %rename(Name) std::function<Ret(__VA_ARGS__)>; 92 %rename(Name##Delegate) std::function<Ret(__VA_ARGS__)>::operator(); 93 94 namespace std { 95 struct function<Ret(__VA_ARGS__)> { 96 // Copy constructor 97 function<Ret(__VA_ARGS__)>(const std::function<Ret(__VA_ARGS__)>&); 98 99 // Name##Delegate operator 100 virtual Ret operator()(__VA_ARGS__) const; 101 102 // Conversion constructor from function pointer 103 function<Ret(__VA_ARGS__)>(Ret(*const)(__VA_ARGS__)); 104 105 %extend { 106 function<Ret(__VA_ARGS__)>(Name##Impl *varin) { 107 return new std::function<Ret(__VA_ARGS__)>([=](FOR_EACH(lvalref,__VA_ARGS__)){ 108 return varin->Name##Delegate(FOR_EACH(unpack,__VA_ARGS__)); 109 }); 110 } 111 } 112 }; 113 } 114 115 %enddef