feature("cs:defaultargs")

We need a way to specify default arguments for functions.  Sometimes
we can just take the arguments straight from c++ literals.  Sometimes
you can't, and need to specify those by hand.

So to make this work we:

 1. Shutoff handling all "defaultargs" variants of methods and constructors
    which have cs:defaultargs defined for their node.  This gets us a single
    constructor or method and skips the variants.
 2. As we emit arguments in the function declaration, check if there is a
    definition for it it from cs:defaultargs, if so, use that.  If not,
    and the method has a cs:defaultargs declaration, then fall back to the
    literal expression from c++.
This commit is contained in:
Chris Hilton 2020-03-17 08:52:24 -05:00 committed by Chris Hilton
parent d2c0105d72
commit b6409b584f
7 changed files with 166 additions and 5 deletions

View File

@ -42,6 +42,7 @@
<li><a href="#CSharp_director_caveats">Director caveats</a>
</ul>
<li><a href="#CSharp_multiple_modules">Multiple modules</a>
<li><a href="#CSharp_argdef">Named arguments</a>
<li><a href="#CSharp_typemap_examples">C# Typemap examples</a>
<ul>
<li><a href="#CSharp_memory_management_member_variables">Memory management when returning references to member variables</a>
@ -2150,7 +2151,52 @@ the <tt>[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrows
if you don't want users to easily stumble upon these so called 'internal workings' of the wrappers.
</p>
<H2><a name="CSharp_typemap_examples">23.8 C# Typemap examples</a></H2>
<H2><a name="CSharp_argdef">23.8 C# Named arguments</a></H2>
<p>
In C++ you can specify default arguments for functions, methods, and constructors. C# offers named arguments. This feature, specific to csharp, lets you bind default argument functions with default arguments in C#. You can also add default arguments to functions which don't have default arguments.
</p>
<p>Please note, using this on a function will turn off the default handling of default arguments, which would create an override for each defaulted argument, so that they could be called the same way they could in C++. So you'll want to specify defaults for every argument when you use this.</p>
<div class="code">
<pre>
%feature("cs:defaultargs") Foo::Foo;
%feature("cs:defaultargs", z=4) Foo::bar;
%feature("cs:defaultargs", x="\"five\"") Foo::zoo;
%inline %{
class Foo {
public:
virtual ~Foo() {}
Foo(int a, int b=1, int c=2)
{
}
int bar(int x, int y=2, int z=3)
{
return x+y+z;
}
int bat(int x=1, int y=2, int z=3)
{
return x+y+z;
}
int zoo(std::string x="four")
{
return (int)x.size();
}
};
%}
</pre>
</div>
<p>For this feature, you first specify the function/constructor you want it to impact. Inside the feature
call you specify each argument you want to override. If you specify none, it will take the literal text from c++
for each argument and apply that in csharp. That often works fine, but when it doesn't you can supply a string literal
with a csharp expression to be used. Or you can supply an int literal, or a float literal. If you want to give a literal
string you need to include an escaped quote at the start and end of the literal: "\"a string\"".</p>
<H2><a name="CSharp_typemap_examples">23.9 C# Typemap examples</a></H2>
This section includes a few examples of typemaps. For more examples, you

View File

@ -1829,6 +1829,7 @@ such as C# and Java,
which don't have optional arguments in the language,
Another restriction of this feature is that it cannot handle default arguments that are not public.
The following example illustrates this:
</p>
<div class="code">
@ -1850,6 +1851,10 @@ evaluates them in the scope of a wrapper function (meaning that the
values have to be public).
</p>
<p>
For C# there is a feature "cs:defaultargs" which allows you to get a single proxy function and lets you specify the arguments in C#.
</p>
<p>
The <tt>compactdefaultargs</tt> feature is automatically turned on when wrapping <a href="SWIG.html#SWIG_default_args">C code with default arguments</a>.
Some target languages will also automatically turn on this feature

View File

@ -121,6 +121,7 @@ CPP_TEST_CASES += \
apply_strings \
argcargvtest \
argout \
csharp_argument_defaults_feature \
array_member \
array_typedef_memberin \
arrayref \

View File

@ -36,6 +36,8 @@ CPP_TEST_CASES = \
nested_scope \
li_boost_intrusive_ptr \
li_std_list \
csharp_argument_defaults_feature \
CPP11_TEST_CASES = \
cpp11_shared_ptr_const \

View File

@ -0,0 +1,27 @@
using System;
using csharp_argument_defaults_featureNamespace;
public class runme {
static void Main() {
Foo foo = new Foo(1);
foo.bar(1); //shutup compiler warning
Foo bar = new Foo(1, c:3);
if(bar.bar(1) != 7)
throw new ApplicationException("bar.bar(1) != 7");
if(bar.bar(1, 4, 4) != 9)
throw new ApplicationException("bar.bar(1, 4, 4) != 9");
if(bar.bar(1, y:3) != 8)
throw new ApplicationException("bar.bar(1, y:3) != 8");
if(bar.bat() != 6)
throw new ApplicationException("bar.bat() != 6");
if(bar.bat(3,3) != 9)
throw new ApplicationException("bar.bat(3,3) != 9");
if(bar.zoo() != 5)
throw new ApplicationException("bar.zoo() != 5");
if(bar.zoo(x:"to") != 2)
throw new ApplicationException("bar.zoo(x:\"to\" != 2");
if(bar.pi() != System.Math.PI)
throw new ApplicationException("bar.pi() != Math.PI");
}
}

View File

@ -0,0 +1,40 @@
%module csharp_argument_defaults_feature
%include "std_string.i"
%feature("cs:defaultargs") Foo::Foo;
%feature("cs:defaultargs", z=4) Foo::bar;
%feature("cs:defaultargs", x="\"fives\"") Foo::zoo;
%feature("cs:defaultargs", value="System.Math.PI") Foo::pi;
//intentionally don't touch bat, leave it to normal handling
%inline %{
#include <string>
class Foo {
public:
virtual ~Foo() {}
Foo(int a, int b=1, int c=2)
{
}
int bar(int x, int y=2, int z=3)
{
return x+y+z;
}
int bat(int x=1, int y=2, int z=3)
{
return x+y+z;
}
int zoo(std::string x="four")
{
return (int)x.size();
}
double pi(double value=3.14)
{
return value;
}
};
%}

View File

@ -15,6 +15,10 @@
#include "cparse.h"
#include <limits.h> // for INT_MAX
#include <ctype.h>
#include <string>
#include <map>
/* Hash type used for upcalls from C/C++ */
typedef DOH UpcallData;
@ -2298,6 +2302,35 @@ public:
return SWIG_OK;
}
void printArgumentDeclaration(Node *n,
Parm *p, String *param_type, String *arg, String *code)
{
String *specifiedoverridekey = NewString("feature:cs:defaultargs:");
Append(specifiedoverridekey, arg);
String *specifiedoverridevalue = Getattr(n, specifiedoverridekey);
if(specifiedoverridevalue)
{
Printf(code, "%s %s=%s", param_type, arg, specifiedoverridevalue);
}
else
{
String *cppvalue = NULL;
//if they've not specified defaultargs, then fall back to
//the normal default handling of specifying one overload per possible
//set of arguments. If they have, then use the default argument from
//c++ as a literal csharp expression.
if(Getattr(n, "feature:cs:defaultargs"))
cppvalue = Getattr(p, "value");
if(cppvalue)
Printf(code, "%s %s=%s", param_type, arg, cppvalue);
else
Printf(code, "%s %s", param_type, arg);
}
Delete(specifiedoverridekey);
}
/* ----------------------------------------------------------------------
* memberfunctionHandler()
* ---------------------------------------------------------------------- */
@ -2376,6 +2409,10 @@ public:
if (Getattr(n, "overload:ignore"))
return;
String *csargdef = Getattr(n, "feature:cs:defaultargs");
if(csargdef && Getattr(n, "defaultargs"))
return;
// Don't generate proxy method for additional explicitcall method used in directors
if (GetFlag(n, "explicitcall"))
return;
@ -2460,7 +2497,6 @@ public:
Printf(imcall, "swigCPtr");
emit_mark_varargs(l);
int gencomma = !static_flag;
/* Output each parameter */
@ -2539,9 +2575,9 @@ public:
Printf(interface_class_code, ", ");
}
gencomma = 2;
Printf(function_code, "%s %s", param_type, arg);
printArgumentDeclaration(n, p, param_type, arg, function_code);
if (is_interface)
Printf(interface_class_code, "%s %s", param_type, arg);
printArgumentDeclaration(n, p, param_type, arg, interface_class_code);
Delete(arg);
Delete(param_type);
@ -2723,6 +2759,10 @@ public:
if (Getattr(n, "overload:ignore"))
return SWIG_OK;
String *csargdef = Getattr(n, "feature:cs:defaultargs");
if(csargdef && Getattr(n, "defaultargs"))
return SWIG_OK;
if (proxy_flag) {
String *overloaded_name = getOverloadedName(n);
String *mangled_overname = Swig_name_construct(getNSpace(), overloaded_name);
@ -2831,7 +2871,7 @@ public:
Printf(helper_code, ", ");
Printf(helper_args, ", ");
}
Printf(function_code, "%s %s", param_type, arg);
printArgumentDeclaration(n, p, param_type, arg, function_code);
Printf(helper_code, "%s %s", param_type, arg);
Printf(helper_args, "%s", cshin ? cshin : arg);
++gencomma;