问题
I found this question, but the one answer was basically, you wouldn't want to do that: Is it possible to add code to an existing method when using Swig to build a C# wrapper for C++ code?
I actually agree with it in the case as described, where the OP was trying to insert code in a way that could be fragile. In my case I am doing exactly what the answer suggested: renaming the method and using %typemap(javacode) to implement the wrapping method.
But I've written this as a macro, and I want to override several methods, so I end up calling %typecode(javacode) multiple times, and only the last wrapping method javacode typemap is active.
The details of the macro are complicated, since it uses variable args to express the signature.
But demonstrating the issue:
%define WRAP(CLASS,METHOD)
%rename(method ## _internal,fullname=1) CLASS::METHOD;
%typemap(javamethodmodifiers) CLASS::METHOD "private";
%typemap(javacode) {
public void METHOD() {
METHOD ## _internal(); // delegate to original
// extra code here
}
} // (note: dont use %{ %} -- need macro evaluation)
%enddef
WRAP(Foo,bar)
WRAP(Foo,baz)
class Foo {
void bar();
void baz();
}
Only the public void baz() { baz_internal(); ... } gets generated. The problem here is that the %rename and %typemap(javamethodmodifiers) are unique since the scope to CLASS::METHOD, but the %typemap(javacode) applies to the whole class. If syntax like
%typemap(javacode,append=true) { // code }
were supported, then this would solve the problem. Is there some technique that could accomplish this? This could make sense for typemaps like javacode or javaimports, whose defaults are empty.
回答1:
I can make a SWIG macro that generates the code you're looking for. It's a bit of a bodge, but it works. The trick was abusing the javaout (with noblock=1) typemap instead of javacode so that it gets applied once per function rather than once per class:
%module test
%define WRAP(CLASS,METHOD)
%rename(METHOD ## _internal,fullname=1) CLASS::METHOD;
%javamethodmodifiers CLASS::METHOD "private";
%typemap(javaout,noblock=1) void CLASS::METHOD {
{
$jnicall;
}
public void METHOD() {
METHOD ## _internal();
// some other bits
}
}
%enddef
WRAP(Foo,bar)
WRAP(Foo,baz)
class Foo {
public:
void bar();
void baz();
};
This generates exactly the code you're looking for, but I suspect you could skip the %rename
entirely and do it all from the javaout typemap. This approach works with SWIG 1.3 and upwards.
I did try another approach with $typemap
and copying typemaps around, but this didn't work out in the end.
If you want to support methods which return things too you'll want to add a third macro argument:
%define WRAP(CLASS,METHOD,RETURN)
%rename(METHOD ## _internal,fullname=1) CLASS::METHOD;
%javamethodmodifiers CLASS::METHOD "private";
%typemap(javaout,noblock=1) RETURN CLASS::METHOD {
$typemap(javaout,RETURN)
public $typemap(jstype,RETURN) METHOD() {
RETURN result = METHOD ## _internal();
// some other bits
return result;
}
}
%enddef
$typemap
there is used to refer to the default, less specialised typemaps to avoid having to duplicate a lot of code for non-primitive/special case returns.
回答2:
Below are three solutions. Change the definition of SOLUTION to either 1, 2 or 3 to try out each of them.
- SOLUTION 1 uses the %proxycode feature added in SWIG-3.0.12, specifically to solve this problem.
- SOLUTION 2 uses a copy/paste/modify of the default typemap approach. This is pretty much the whole purpose of typemaps, that is, taking an existing one and customizing it to get the generated code you want.
- SOLUTION 3 re-uses the contents of another (the default) typemap in the user typemap.
%module example
#define SOLUTION 1
#if SOLUTION==1
%define WRAP(CLASS,METHOD)
%rename(METHOD ## _internal) CLASS::METHOD;
%typemap(javamethodmodifiers) CLASS::METHOD "private";
//%typemap(javacode) {
%extend CLASS {
%proxycode %{
public void METHOD() {
METHOD ## _internal(); // delegate to original
// extra code here
System.out.println("extra code in " + #METHOD + " SOLUTION 1");
}
%}
}
%enddef
#elif SOLUTION==2
%define WRAP(CLASS,METHOD)
%typemap(javaout) void CLASS::METHOD {
// Next line is copied from java.swg: %typemap(javaout) void
$jnicall;
// extra code here
System.out.println("extra code in " + #METHOD + " SOLUTION 2");
}
%enddef
#elif SOLUTION==3
%define WRAP(CLASS,METHOD)
%typemap(javaout) void CLASS::METHOD {
// Next line re-uses/includes the typemap in java.swg: %typemap(javaout) void
$typemap(javaout, void);
// extra code here
System.out.println("extra code in " + #METHOD + " SOLUTION 3");
}
%enddef
#else
#error "Bad SOLUTION"
#endif
WRAP(Foo,bar)
WRAP(Foo,baz)
%inline %{
class Foo {
public:
void bar();
void baz();
};
%}
%{
#include <iostream>
void Foo::bar() { std::cout << "Foo::bar " << std::endl; }
void Foo::baz() { std::cout << "Foo::baz " << std::endl; }
%}
来源:https://stackoverflow.com/questions/12103206/is-it-possible-to-add-text-to-an-existing-typemap-in-swig