Add in pre, post and cshin attributes for the csin typemap

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@9678 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
William S Fulton 2007-04-17 22:45:17 +00:00
parent f63d3ad5ad
commit 0090cac1d0
2 changed files with 340 additions and 41 deletions

View File

@ -28,6 +28,7 @@
<ul>
<li><a href="#csharp_memory_management_member_variables">Memory management when returning references to member variables</a>
<li><a href="#csharp_memory_management_objects">Memory management for objects passed to the C++ layer</a>
<li><a href="#csharp_date_marshalling">Date marshalling using the csin typemap and associated attributes</a>
</ul>
</ul>
</div>
@ -195,7 +196,7 @@ $jnicall -&gt; $imcall
<li>
<p>
The intermediary classname has <tt>PINVOKE</tt> appended after the module name instead of <tt>JNI</tt>, for example <tt>modulenamePINVOKE</tt>.
Unlike the "javain" typemap, the "csin" typemap does not support the 'pgcpp' attribute as the C# module does not have a premature garbage collection prevention parameter. The "csin" typemap supports an additional optional attribute called 'cshin'. It should contain the parameter type and name whenever a <a href="Java.html#java_constructor_helper_function">constructor helper function</a> is generated due to the 'pre' or 'post' attributes. Please see the <a href="#csharp_date_marshalling">Date marshalling example</a> for further understanding.
</p>
</li>
@ -326,6 +327,12 @@ they can be added using the 'csvarin' and 'csvarout' typemaps respectively.
</p>
</li>
<li>
<p>
The intermediary classname has <tt>PINVOKE</tt> appended after the module name instead of <tt>JNI</tt>, for example <tt>modulenamePINVOKE</tt>.
</p>
</li>
<li>
<p>
The <tt>%csmethodmodifiers</tt> feature can also be applied to variables as well as methods.
@ -450,6 +457,7 @@ typedef enum {
SWIG_CSharpArithmeticException,
SWIG_CSharpDivideByZeroException,
SWIG_CSharpIndexOutOfRangeException,
SWIG_CSharpInvalidCastException,
SWIG_CSharpInvalidOperationException,
SWIG_CSharpIOException,
SWIG_CSharpNullReferenceException,
@ -1241,41 +1249,8 @@ void SwigDirector_Base::BaseBoolMethod(Base const &amp;b, bool flag) {
<p>
There are a few gotchas with directors.
The first is that the base class virtual method should not be called directly otherwise a stack overflow will occur due to recursive calls.
This might be fixed in a future version of SWIG, but is likely to slow down virtual methods calls.
For example, given <tt>Base</tt> as a director enabled class:
</p>
<div class="code">
<pre>
class Base {
public:
virtual ~Base();
virtual unsigned int UIntMethod(unsigned int x);
};
</pre>
</div>
<p>
Do not directly call the base method from a C# derived class:
</p>
<div class="code">
<pre>
public class CSharpDerived : Base
{
public override uint UIntMethod(uint x)
{
return base.UIntMethod(x);
}
}
</pre>
</div>
<p>
Secondly, if default parameters are used, it is recommended to follow a pattern of always calling a single method in any C# derived class.
There is a subtle gotcha with directors.
If default parameters are used, it is recommended to follow a pattern of always calling a single method in any C# derived class.
An example will clarify this and the reasoning behind the recommendation. Consider the following C++ class wrapped as a director class:
</p>
@ -1569,6 +1544,183 @@ The 'cscode' typemap simply adds in the specified code into the C# proxy class.
</div>
<H3><a name="csharp_date_marshalling"></a>17.5.3 Date marshalling using the csin typemap and associated attributes</H3>
<p>
The <a href="Java.html#nan_exception_typemap">NaN Exception example</a> is a simple example of the "javain" typemap and its 'pre' attribute.
This example demonstrates how a C++ date class, say <tt>CDate</tt>, can be mapped onto the standard .NET date class,
<tt>System.DateTime</tt> by using the 'pre', 'post' and 'pgcppname' attributes of the "csin" typemap (the C# equivalent to the "javain" typemap).
The example is an equivalent to the <a href="Java.html#java_date_marshalling">Java Date marshalling example</a>.
The idea is that the <tt>System.DateTime</tt> is used wherever the C++ API uses a <tt>CDate</tt>.
Let's assume the code being wrapped is as follows:
</p>
<div class="code">
<pre>
class CDate {
public:
CDate(int year, int month, int day);
int getYear();
int getMonth();
int getDay();
...
};
struct Action {
static int doSomething(const CDate &amp;dateIn, CDate &amp;dateOut);
Action(const CDate &amp;date, CDate &amp;dateOut);
};
</pre>
</div>
<p>
Note that <tt>dateIn</tt> is const and therefore read only and <tt>dateOut</tt> is a non-const output type.
</p>
<p>
First let's look at the code that is generated by default, where the C# proxy class <tt>CDate</tt> is used in the proxy interface:
</p>
<div class="code">
<pre>
public class Action : IDisposable {
...
public Action(CDate dateIn, CDate dateOut)
: this(examplePINVOKE.new_Action(CDate.getCPtr(dateIn), CDate.getCPtr(dateOut)), true) {
if (examplePINVOKE.SWIGPendingException.Pending)
throw examplePINVOKE.SWIGPendingException.Retrieve();
}
public int doSomething(CDate dateIn, CDate dateOut) {
int ret = examplePINVOKE.Action_doSomething(swigCPtr,
CDate.getCPtr(dateIn),
CDate.getCPtr(dateOut));
if (examplePINVOKE.SWIGPendingException.Pending)
throw examplePINVOKE.SWIGPendingException.Retrieve();
return ret;
}
}
</pre>
</div>
<p>
The <tt>CDate &amp;</tt> and <tt>const CDate &amp;</tt> C# code is generated from the following two default typemaps:
</p>
<div class="code">
<pre>
%typemap(jstype) SWIGTYPE &amp; "$csclassname"
%typemap(csin) SWIGTYPE &amp; "$csclassname.getCPtr($csinput)"
</pre>
</div>
<p>
where '$csclassname' is translated into the proxy class name, <tt>CDate</tt> and '$csinput' is translated into the name of the parameter, eg <tt>dateIn</tt>.
From C#, the intention is then to call into a modifed API with something like:
</p>
<div class="code">
<pre>
System.DateTime dateIn = new System.DateTime(2011, 4, 13);
System.DateTime dateOut = new System.DateTime();
// Note in calls below, dateIn remains unchanged and dateOut
// is set to a new value by the C++ call
Action action = new Action(dateIn, ref dateOut);
dateIn = new System.DateTime(2012, 7, 14);
</pre>
</div>
<p>
To achieve this mapping, we need to alter the default code generation slightly so that at the C# layer,
a <tt>System.DateTime</tt> is converted into a <tt>CDate</tt>.
The intermediary layer will still take a pointer to the underlying <tt>CDate</tt> class.
The typemaps to achieve this are shown below.
</p>
<div class="code">
<pre>
%typemap(cstype) const CDate&amp; "System.DateTime"
%typemap(csin,
pre=" CDate temp$csinput = new CDate($csinput.Year, $csinput.Month, $csinput.Day);")
const CDate &amp;
"$csclassname.getCPtr(temp$csinput)"
%typemap(cstype) CDate&amp; "ref System.DateTime"
%typemap(csin,
pre=" CDate temp$csinput = new CDate();",
post=" $csinput = new System.DateTime(temp$csinput.getYear(),"
" temp$csinput.getMonth(), temp$csinput.getDay(), 0, 0, 0);",
cshin="ref $csinput") CDate &amp;
"$csclassname.getCPtr(temp$csinput)"
</pre>
</div>
<p>
The resulting generated proxy code in the <tt>Action</tt> class follows:
</p>
<div class="code">
<pre>
public class Action : IDisposable {
...
public int doSomething(System.DateTime dateIn, ref System.DateTime dateOut) {
CDate tempdateIn = new CDate(dateIn.Year, dateIn.Month, dateIn.Day);
CDate tempdateOut = new CDate();
try {
int ret = examplePINVOKE.Action_doSomething(swigCPtr,
CDate.getCPtr(tempdateIn),
CDate.getCPtr(tempdateOut));
if (examplePINVOKE.SWIGPendingException.Pending)
throw examplePINVOKE.SWIGPendingException.Retrieve();
return ret;
} finally {
dateOut = new System.DateTime(tempdateOut.getYear(),
tempdateOut.getMonth(), tempdateOut.getDay(), 0, 0, 0);
}
}
static private IntPtr SwigConstructAction(System.DateTime dateIn, ref System.DateTime dateOut) {
CDate tempdateIn = new CDate(dateIn.Year, dateIn.Month, dateIn.Day);
CDate tempdateOut = new CDate();
try {
return examplePINVOKE.new_Action(CDate.getCPtr(tempdateIn), CDate.getCPtr(tempdateOut));
} finally {
dateOut = new System.DateTime(tempdateOut.getYear(),
tempdateOut.getMonth(), tempdateOut.getDay(), 0, 0, 0);
}
}
public Action(System.DateTime dateIn, ref System.DateTime dateOut)
: this(Action.SwigConstructAction(dateIn, ref dateOut), true) {
if (examplePINVOKE.SWIGPendingException.Pending)
throw examplePINVOKE.SWIGPendingException.Retrieve();
}
}
</pre>
</div>
<p>
A few things to note:
</p>
<ul>
<li> The "cstype" typemap has changed the parameter type to <tt>System.DateTime</tt> instead of the default generated <tt>CDate</tt> proxy.
<li> The non-const <tt>CDate &amp;</tt> type is marshalled as a reference parameter in C# as the date cannot be explicitly set once the object has been created, so a new object is created instead.
<li> The code in the 'pre' attribute appears before the intermediary call (<tt>examplePINVOKE.new_Action</tt> / <tt>examplePINVOKE.Action_doSomething</tt>).
<li> The code in the 'post' attribute appears after the intermediary call.
<li> A try .. finally block is generated with the intermediary call in the try block and 'post' code in the finally block.
The alternative of just using a temporary variable for the return value from the intermediary call and the 'post' code being inserted before the
return statement is not possible given that the intermediary call and method return comes from a single source (the "csout" typemap).
<li> The temporary variables in the "csin" typemaps are called <tt>temp$csin</tt>, where "$csin" is replaced with the parameter name.
"$csin" is used to mangle the variable name so that more than one <tt>CDate &amp;</tt> type can be used as a parameter in a method, otherwise two or
more local variables with the same name would be generated.
<li> The use of the "csin" typemap causes a constructor helper function (<tt>SwigConstructAction</tt>) to be generated.
This allows C# code to be called before the intermediary call made in the constructor initialization list.
<li> The 'cshin' attribute is required for the <tt>SwigConstructAction</tt> constructor helper function so that the 2nd parameter is declared as <tt>ref dateOut</tt> instead of just <tt>dateOut</tt>.
</ul>
</body>
</html>

View File

@ -1854,6 +1854,8 @@ public:
String *return_type = NewString("");
String *function_code = NewString("");
bool setter_flag = false;
String *pre_code = NewString("");
String *post_code = NewString("");
if (!proxy_flag)
return;
@ -1983,6 +1985,22 @@ public:
if ((tm = Getattr(p, "tmap:csin"))) {
substituteClassname(pt, tm);
Replaceall(tm, "$csinput", arg);
String *pre = Getattr(p, "tmap:csin:pre");
if (pre) {
substituteClassname(pt, pre);
Replaceall(pre, "$csinput", arg);
if (Len(pre_code) > 0)
Printf(pre_code, "\n");
Printv(pre_code, pre, NIL);
}
String *post = Getattr(p, "tmap:csin:post");
if (post) {
substituteClassname(pt, post);
Replaceall(post, "$csinput", arg);
if (Len(post_code) > 0)
Printf(post_code, "\n");
Printv(post_code, post, NIL);
}
Printv(imcall, tm, NIL);
} else {
Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
@ -2005,6 +2023,24 @@ public:
// Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in proxy class)
if ((tm = Swig_typemap_lookup_new("csout", n, "", 0))) {
excodeSubstitute(n, tm, "csout", n);
bool is_pre_code = Len(pre_code) > 0;
bool is_post_code = Len(post_code) > 0;
if (is_pre_code || is_post_code) {
Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
if (is_post_code) {
Insert(tm, 0, "\n try ");
Printv(tm, " finally {\n", post_code, "\n }", NIL);
} else {
Insert(tm, 0, "\n ");
}
if (is_pre_code) {
Insert(tm, 0, pre_code);
Insert(tm, 0, "\n");
}
Insert(tm, 0, "{");
Printf(tm, "\n }");
}
if (GetFlag(n, "feature:new"))
Replaceall(tm, "$owner", "true");
else
@ -2033,7 +2069,6 @@ public:
Delete(excode);
}
Replaceall(tm, "$imcall", imcall);
excodeSubstitute(n, tm, "csout", n);
} else {
Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
}
@ -2097,6 +2132,8 @@ public:
Printv(proxy_class_code, function_code, NIL);
}
Delete(pre_code);
Delete(post_code);
Delete(function_code);
Delete(return_type);
Delete(imcall);
@ -2113,6 +2150,11 @@ public:
Parm *p;
int i;
String *function_code = NewString("");
String *helper_code = NewString(""); // Holds code for the constructor helper method generated only when the csin typemap has code in the pre or post attributes
String *helper_args = NewString("");
String *pre_code = NewString("");
String *post_code = NewString("");
String *im_return_type = NewString("");
bool feature_director = (parentNode(n) && Swig_directorclass(n));
Language::constructorHandler(n);
@ -2127,11 +2169,22 @@ public:
String *imcall = NewString("");
const String *csattributes = Getattr(n, "feature:cs:attributes");
if (csattributes)
if (csattributes) {
Printf(function_code, " %s\n", csattributes);
Printf(helper_code, " %s\n", csattributes);
}
const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);
tm = Getattr(n, "tmap:imtype"); // typemaps were attached earlier to the node
String *imtypeout = Getattr(n, "tmap:imtype:out"); // the type in the imtype typemap's out attribute overrides the type in the typemap
if (imtypeout)
tm = imtypeout;
Printf(im_return_type, "%s", tm);
Printf(function_code, " %s %s(", methodmods, proxy_class_name);
Printf(helper_code, " static private %s SwigConstruct%s(", im_return_type, proxy_class_name);
Printv(imcall, imclass_name, ".", mangled_overname, "(", NIL);
/* Attach the non-standard typemaps to the parameter list */
@ -2174,22 +2227,48 @@ public:
Printf(imcall, ", ");
String *arg = makeParameterName(n, p, i, false);
String *cshin = 0;
// Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
if ((tm = Getattr(p, "tmap:csin"))) {
substituteClassname(pt, tm);
Replaceall(tm, "$csinput", arg);
String *pre = Getattr(p, "tmap:csin:pre");
if (pre) {
substituteClassname(pt, pre);
Replaceall(pre, "$csinput", arg);
if (Len(pre_code) > 0)
Printf(pre_code, "\n");
Printv(pre_code, pre, NIL);
}
String *post = Getattr(p, "tmap:csin:post");
if (post) {
substituteClassname(pt, post);
Replaceall(post, "$csinput", arg);
if (Len(post_code) > 0)
Printf(post_code, "\n");
Printv(post_code, post, NIL);
}
cshin = Getattr(p, "tmap:csin:cshin");
if (cshin)
Replaceall(cshin, "$csinput", arg);
Printv(imcall, tm, NIL);
} else {
Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
}
/* Add parameter to proxy function */
if (gencomma)
if (gencomma) {
Printf(function_code, ", ");
Printf(helper_code, ", ");
Printf(helper_args, ", ");
}
Printf(function_code, "%s %s", param_type, arg);
Printf(helper_code, "%s %s", param_type, arg);
Printf(helper_args, "%s", cshin ? cshin : arg);
++gencomma;
Delete(cshin);
Delete(arg);
Delete(param_type);
p = Getattr(p, "tmap:in:next");
@ -2198,6 +2277,7 @@ public:
Printf(imcall, ")");
Printf(function_code, ")");
Printf(helper_code, ")");
/* Insert the csconstruct typemap, doing the replacement for $directorconnect, as needed */
Hash *attributes = NewHash();
@ -2221,10 +2301,40 @@ public:
Printv(function_code, " ", construct_tm, NIL);
}
Replaceall(function_code, "$imcall", imcall);
excodeSubstitute(n, function_code, "csconstruct", attributes);
bool is_pre_code = Len(pre_code) > 0;
bool is_post_code = Len(post_code) > 0;
if (is_pre_code || is_post_code) {
Printf(helper_code, " {\n");
if (is_pre_code) {
Printv(helper_code, pre_code, "\n", NIL);
}
if (is_post_code) {
Printf(helper_code, " try {\n");
Printv(helper_code, " return ", imcall, ";\n", NIL);
Printv(helper_code, " } finally {\n", post_code, "\n }", NIL);
} else {
Printv(helper_code, " return ", imcall, ";", NIL);
}
Printf(helper_code, "\n }\n");
String *helper_name = NewStringf("%s.SwigConstruct%s(%s)", proxy_class_name, proxy_class_name, helper_args);
String *im_outattribute = Getattr(n, "tmap:imtype:outattributes");
if (im_outattribute)
Printf(proxy_class_code, " %s\n", im_outattribute);
Printv(proxy_class_code, helper_code, "\n", NIL);
Replaceall(function_code, "$imcall", helper_name);
Delete(helper_name);
} else {
Replaceall(function_code, "$imcall", imcall);
}
Printv(proxy_class_code, function_code, "\n", NIL);
Delete(helper_args);
Delete(im_return_type);
Delete(pre_code);
Delete(post_code);
Delete(construct_tm);
Delete(attributes);
Delete(overloaded_name);
@ -2340,6 +2450,8 @@ public:
String *overloaded_name = getOverloadedName(n);
String *func_name = NULL;
bool setter_flag = false;
String *pre_code = NewString("");
String *post_code = NewString("");
if (l) {
if (SwigType_type(Getattr(l, "type")) == T_VOID) {
@ -2425,6 +2537,22 @@ public:
if ((tm = Getattr(p, "tmap:csin"))) {
substituteClassname(pt, tm);
Replaceall(tm, "$csinput", arg);
String *pre = Getattr(p, "tmap:csin:pre");
if (pre) {
substituteClassname(pt, pre);
Replaceall(pre, "$csinput", arg);
if (Len(pre_code) > 0)
Printf(pre_code, "\n");
Printv(pre_code, pre, NIL);
}
String *post = Getattr(p, "tmap:csin:post");
if (post) {
substituteClassname(pt, post);
Replaceall(post, "$csinput", arg);
if (Len(post_code) > 0)
Printf(post_code, "\n");
Printv(post_code, post, NIL);
}
Printv(imcall, tm, NIL);
} else {
Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
@ -2446,13 +2574,30 @@ public:
// Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in module class)
if ((tm = Swig_typemap_lookup_new("csout", n, "", 0))) {
excodeSubstitute(n, tm, "csout", n);
bool is_pre_code = Len(pre_code) > 0;
bool is_post_code = Len(post_code) > 0;
if (is_pre_code || is_post_code) {
Replaceall(tm, "\n ", "\n "); // add extra indentation to code in typemap
if (is_post_code) {
Insert(tm, 0, "\n try ");
Printv(tm, " finally {\n", post_code, "\n }", NIL);
} else {
Insert(tm, 0, "\n ");
}
if (is_pre_code) {
Insert(tm, 0, pre_code);
Insert(tm, 0, "\n");
}
Insert(tm, 0, "{");
Printf(tm, "\n }");
}
if (GetFlag(n, "feature:new"))
Replaceall(tm, "$owner", "true");
else
Replaceall(tm, "$owner", "false");
substituteClassname(t, tm);
Replaceall(tm, "$imcall", imcall);
excodeSubstitute(n, tm, "csout", n);
} else {
Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
}
@ -2516,6 +2661,8 @@ public:
Printv(module_class_code, function_code, NIL);
}
Delete(pre_code);
Delete(post_code);
Delete(function_code);
Delete(return_type);
Delete(imcall);