Extend C # proxy class for C ++ template

TL; DR: How do I access the "T" template type in C # for SWIG?

Let's say I have the following template class in C ++ with the Validate function:

 template<typename T> struct MyTemplateClass { bool Validate(T* _in) { //... } // ... other stuff... MyTemplateClass is also a container for T* }; 

Let's say I create an instance of a class for many objects:

 %template(MyTemplateClassFoo) MyTemplateClass<Foo> %template(MyTemplateClassBar) MyTemplateClass<Bar> // etc. 

In C #, I want the Validate function to also check the ownership of the memory. That is, _in.swigCMemOwn is true or false , so I would like the C # wrapper to look something like this (for MyTemplateClassFoo )

 public class MyTemplateClassFoo{ public bool Validate(Foo _in) { bool ret = _in.swigCMemOwn && ModuleCLRPINVOKE.MyTemplateClassFoo_Validate(swigcPtr, Foo.getCPtr(_in)); // SWIGEXCODE stuff return ret; } // ... } 

The problem is that if I want to write my own Validate function, I don't know what type of _in will be. In Python, I could accomplish this using feature("shadow") or pythonprepend and pythonappend

Until I got to:

  • Make Validate private using %csmethodmodifiers MyTemplateClass::Validate "private";
  • Rename Validate to InternalValidate through %rename(InternalValidate, fullname=1) "MyTemplateClass::Validate";
  • Use %typemap(cscode) to add a new function that will call InternalValidate :

the code:

 %typemap(cscode) MyTemplateClass %{ public bool Validate(/*Type?*/ _in) { return _in.swigCMemOwn && InternalValidate(_in); } %} 

But I do not know what I should specify for /*Type?*/ . I tried T , T* , typemap(cstype, T) , but it doesn't seem like there is a special swig variable like $csargtype , which I can use.

I tried to see how SWIG wraps std::vector , and it seems that maybe they define a macro and then somehow call it for each specialization of the vector? I think I could live with it, but I didn’t like it.

+1
source share
1 answer

To work with these examples, I created the following header file:

 template<typename T> struct MyTemplateClass { bool Validate(T* _in) { return false; } // ... other stuff... MyTemplateClass is also a container for T* }; struct Foo {}; 

The good news is that you can actually generate the code you request is much simpler than what you tried. We can just use the csout map map, which is valid only for Validate , and we are good to go:

 %module test %{ #include "test.hh" %} %typemap(csout, excode=SWIGEXCODE) bool Validate { // referring to _in by name is a bit of a hack here, but it works... bool ret = _in.swigCMemOwn && $imcall;$excode return ret; } %include "test.hh" %template(MyTemplateClassInt) MyTemplateClass<int>; %template(MyTemplateClassFoo) MyTemplateClass<Foo>; 

For completeness, although let us look at the original question as posed. First, simplify things by making MyTemplateClass actually not a template (i.e. Comment out line 1 of test.hh and add a typedef for T somewhere instead).

In this case, what you were trying to do pretty much works using $typemap(cstype, T) to search for the C # type used for that type during SWIG compilation:

 %module test %{ #include "test.hh" %} %typemap(cscode) MyTemplateClass %{ public bool Validate($typemap(cstype, T) in) { return in.swigCMemOwn && InternalValidate(in); } %} %rename(InternalValidate) Validate; %include "test.hh" 

However, when we return to the template again, the generated code is incorrect, created by Validate :

 public bool Validate(SWIGTYPE_p_T in) 

This is because SWIG (at least 3.0, from Ubuntu 14.04) does not know anything about T in this context - the template is replaced incorrectly. I'm not quite sure if this is a mistake or an expected behavior, but in any case this is a problem for us.

Interestingly, however, if you want to write a sample cscode map inside the template definition, which SWIG sees that the replacement works:

 %module test %{ #include "test.hh" %} %rename(InternalValidate) Validate; template<typename T> struct MyTemplateClass { bool Validate(T* _in) { return false; } // ... other stuff... MyTemplateClass is also a container for T* %typemap(cscode) MyTemplateClass %{ public bool Validate($typemap(cstype, T) in) { return in.swigCMemOwn && InternalValidate(in); } %} }; struct Foo {}; %template(MyTemplateClassInt) MyTemplateClass<int>; %template(MyTemplateClassFoo) MyTemplateClass<Foo>; 

In the above interface, the type for T correctly replaced by the output. Therefore, if you agree to accept duplication between the .i file and the actual header files that you use in the library, then this is enough. You can also edit the header file itself and mix SWIG and C ++ with it, the following modified .hh test will achieve the same result:

 template<typename T> struct MyTemplateClass { bool Validate(T* _in) { return false; } // ... other stuff... MyTemplateClass is also a container for T* #ifdef SWIG %typemap(cscode) MyTemplateClass %{ public bool Validate($typemap(cstype, T) in) { return in.swigCMemOwn && InternalValidate(in); } %} #endif }; struct Foo {}; 

This works because SWIG defines the macro for the SWIG preprocessor, but it will not be defined during normal C ++ compilation, so everything is fine. Personally, I don't like this - I would rather keep the C ++ and SWIG bits logically separated by a clean border.

If you do not want to duplicate this, and cannot / will not simply edit the header file, all is not lost. We can (ab) use %extend so that we do the same:

 %module test %{ #include "test.hh" %} %rename(InternalValidate) Validate; %include "test.hh" %extend MyTemplateClass { %typemap(cscode) MyTemplateClass %{ public bool Validate($typemap(cstype, T) in) { return in.swigCMemOwn && InternalValidate(in); } %} } %template(MyTemplateClassInt) MyTemplateClass<int>; %template(MyTemplateClassFoo) MyTemplateClass<Foo>; 

What works again.

The final solution is that if you have a typedef inside a template that just uses T, for example:

 template<typename T> struct MyTemplateClass { typedef T type; //... 

Then the following works refer to typedef as $1_basetype::type :

 %module test %{ #include "test.hh" %} %rename(InternalValidate) Validate; %typemap(cscode) MyTemplateClass %{ public bool Validate($typemap(cstype, $1_basetype::type) in) { return in.swigCMemOwn && InternalValidate(in); } %} %include "test.hh" %template(MyTemplateClassInt) MyTemplateClass<int>; %template(MyTemplateClassFoo) MyTemplateClass<Foo>; 

Thus, although there is a simple way that looks as if it should work, it seems that there are still many options that can achieve the desired result.

+1
source

Source: https://habr.com/ru/post/905437/


All Articles