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
posted on 2024-09-25 10:37  秦道友  阅读(19)  评论(0编辑  收藏  举报