diff --git a/Doc/Manual/CSharp.html b/Doc/Manual/CSharp.html index b911060aa..d24427010 100644 --- a/Doc/Manual/CSharp.html +++ b/Doc/Manual/CSharp.html @@ -29,6 +29,7 @@
  • Memory management when returning references to member variables
  • Memory management for objects passed to the C++ layer
  • Date marshalling using the csin typemap and associated attributes +
  • A date example demonstrating marshalling of C# properties
  • Turning wrapped classes into partial classes
  • Extending proxy classes with additional C# code @@ -198,7 +199,7 @@ $jnicall -> $imcall
  • -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 constructor helper function is generated due to the 'pre' or 'post' attributes. Please see the Date marshalling example for further understanding. +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 constructor helper function is generated due to the 'pre' or 'post' attributes. Note that 'pre', 'post' and 'cshin' attributes are not used for marshalling the property set. Please see the Date marshalling example and Date marshalling of properties example for further understanding.

  • @@ -326,6 +327,8 @@ public class AClass : IDisposable {

    If C# attributes need adding to the set or get part of C# properties, when wrapping C/C++ variables, they can be added using the 'csvarin' and 'csvarout' typemaps respectively. +Note that the type used for the property is specified in the 'cstype' typemap. +If the 'out' attribute exists in this typemap, then the type used is from the 'out' attribute.

    @@ -1611,7 +1614,7 @@ The CDate & and const CDate & C# code is generated fro
    -%typemap(jstype) SWIGTYPE & "$csclassname"
    +%typemap(cstype) SWIGTYPE & "$csclassname"
     %typemap(csin) SWIGTYPE & "$csclassname.getCPtr($csinput)"
     
    @@ -1628,7 +1631,7 @@ 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); +Action action = new Action(dateIn, out dateOut); dateIn = new System.DateTime(2012, 7, 14); @@ -1648,12 +1651,12 @@ The typemaps to achieve this are shown below. const CDate & "$csclassname.getCPtr(temp$csinput)" -%typemap(cstype) CDate& "ref System.DateTime" +%typemap(cstype) CDate& "out 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 & + cshin="out $csinput") CDate & "$csclassname.getCPtr(temp$csinput)" @@ -1667,7 +1670,7 @@ The resulting generated proxy code in the Action class follows:
     public class Action : IDisposable {
       ...
    -  public int doSomething(System.DateTime dateIn, ref System.DateTime dateOut) {
    +  public int doSomething(System.DateTime dateIn, out System.DateTime dateOut) {
         CDate tempdateIn = new CDate(dateIn.Year, dateIn.Month, dateIn.Day);
         CDate tempdateOut = new CDate();
         try {
    @@ -1683,7 +1686,7 @@ public class Action : IDisposable {
         }
       }
     
    -  static private IntPtr SwigConstructAction(System.DateTime dateIn, ref System.DateTime dateOut) {
    +  static private IntPtr SwigConstructAction(System.DateTime dateIn, out System.DateTime dateOut) {
         CDate tempdateIn = new CDate(dateIn.Year, dateIn.Month, dateIn.Day);
         CDate tempdateOut = new CDate();
         try {
    @@ -1694,8 +1697,8 @@ public class Action : IDisposable {
         }
       }
     
    -  public Action(System.DateTime dateIn, ref System.DateTime dateOut) 
    -      : this(Action.SwigConstructAction(dateIn, ref dateOut), true) {
    +  public Action(System.DateTime dateIn, out System.DateTime dateOut) 
    +      : this(Action.SwigConstructAction(dateIn, out dateOut), true) {
         if (examplePINVOKE.SWIGPendingException.Pending) 
           throw examplePINVOKE.SWIGPendingException.Retrieve();
       }
    @@ -1719,10 +1722,171 @@ A few things to note:
       more local variables with the same name would be generated.
       
  • The use of the "csin" typemap causes a constructor helper function (SwigConstructAction) to be generated. This allows C# code to be called before the intermediary call made in the constructor initialization list. -
  • The 'cshin' attribute is required for the SwigConstructAction constructor helper function so that the 2nd parameter is declared as ref dateOut instead of just dateOut. +
  • The 'cshin' attribute is required for the SwigConstructAction constructor helper function so that the 2nd parameter is declared as out dateOut instead of just dateOut. -

    17.5.4 Turning wrapped classes into partial classes

    +

    +So far we have considered the date as an input only and an output only type. +Now let's consider CDate * used as an input/output type. Consider the following C++ function which modifies the date passed in: +

    + +
    +
    +void addYears(CDate *pDate, int years) {
    +  *pDate = CDate(pDate->getYear() + years, pDate->getMonth(), pDate->getDay());
    +}
    +
    +
    + +

    +If usage of CDate * commonly follows this input/output pattern, usage from C# like the following +

    + +
    +
    +System.DateTime christmasEve = new System.DateTime(2000, 12, 24);
    +example.addYears(ref christmasEve, 10); // christmasEve now contains 2010-12-24
    +
    +
    + +

    +will be possible with the following CDate * typemaps +

    + +
    +
    +%typemap(cstype, out="System.DateTime") CDate * "ref System.DateTime"
    +
    +%typemap(csin,
    +         pre="    CDate temp$csinput = new CDate($csinput.Year, $csinput.Month, $csinput.Day);",
    +         post="      $csinput = new System.DateTime(temp$csinput.getYear(),"
    +              " temp$csinput.getMonth(), temp$csinput.getDay(), 0, 0, 0);", 
    +         cshin="ref $csinput") CDate *
    +         "$csclassname.getCPtr(temp$csinput)"
    +
    +
    + +

    +Globals are wrapped by the module class and for a module called example, the typemaps result in the following code: +

    + +
    +
    +public class example {
    +  public static void addYears(ref System.DateTime pDate, int years) {
    +    CDate temppDate = new CDate(pDate.Year, pDate.Month, pDate.Day);
    +    try {
    +      examplePINVOKE.addYears(CDate.getCPtr(temppDate), years);
    +    } finally {
    +      pDate = new System.DateTime(temppDate.getYear(), temppDate.getMonth(), temppDate.getDay(), 0, 0, 0);
    +    }
    +  }
    +  ...
    +}
    +
    +
    + +

    17.5.4 A date example demonstrating marshalling of C# properties

    + + +

    +The previous section looked at converting a C++ date class to System.DateTime for parameters. +This section extends this idea so that the correct marshalling is obtained when wrapping C++ variables. +Consider the same CDate class from the previous section and a global variable: +

    + +
    +
    +CDate ImportantDate = CDate(1999, 12, 31);
    +
    +
    + +

    +The aim is to use System.DateTime from C# when accessing this date as shown in the following usage where the module name is 'example': +

    + +
    +
    +example.ImportantDate = new System.DateTime(2000, 11, 22);
    +System.DateTime importantDate = example.ImportantDate;
    +Console.WriteLine("Important date: " + importantDate);
    +
    +
    + +

    +When SWIG wraps a variable that is a class/struct/union, it is wrapped using a pointer to the type for the reasons given in Stucture data members. +The typemap type required is thus CDate *. Given that the previous section already designed CDate * typemaps, we'll use those same typemaps plus the 'csvarin' and 'csvarout' typemaps. + +

    +
    +%typemap(cstype, out="System.DateTime") CDate * "ref System.DateTime"
    +
    +%typemap(csin,
    +         pre="    CDate temp$csinput = new CDate($csinput.Year, $csinput.Month, $csinput.Day);",
    +         post="      $csinput = new System.DateTime(temp$csinput.getYear(),"
    +              " temp$csinput.getMonth(), temp$csinput.getDay(), 0, 0, 0);", 
    +         cshin="ref $csinput") CDate *
    +         "$csclassname.getCPtr(temp$csinput)"
    +
    +%typemap(csvarin, excode=SWIGEXCODE2) CDate * %{
    +    /* csvarin typemap code */
    +    set {
    +      CDate temp$csinput = new CDate($csinput.Year, $csinput.Month, $csinput.Day);
    +      $imcall;$excode
    +    } %}
    +
    +%typemap(csvarout, excode=SWIGEXCODE2) CDate * %{
    +    /* csvarout typemap code */
    +    get {
    +      IntPtr cPtr = $imcall;
    +      CDate tempDate = (cPtr == IntPtr.Zero) ? null : new CDate(cPtr, $owner);$excode
    +      return new System.DateTime(tempDate.getYear(), tempDate.getMonth(), tempDate.getDay(),
    +                                 0, 0, 0);
    +    } %}
    +
    +
    + +

    +For a module called example, the typemaps result in the following code: +

    + +
    +
    +public class example {
    +  public static System.DateTime ImportantDate {
    +    /* csvarin typemap code */
    +    set {
    +      CDate tempvalue = new CDate(value.Year, value.Month, value.Day);
    +      examplePINVOKE.ImportantDate_set(CDate.getCPtr(tempvalue));
    +    } 
    +    /* csvarout typemap code */
    +    get {
    +      IntPtr cPtr = examplePINVOKE.ImportantDate_get();
    +      CDate tempDate = (cPtr == IntPtr.Zero) ? null : new CDate(cPtr, false);
    +      return new System.DateTime(tempDate.getYear(), tempDate.getMonth(), tempDate.getDay(),
    +                                 0, 0, 0);
    +    } 
    +  }
    +  ...
    +}
    +
    +
    + +

    +Some points to note: +

    + + + + +

    17.5.5 Turning wrapped classes into partial classes

    @@ -1822,7 +1986,7 @@ demonstrating that the class contains methods calling both unmanaged code - The following example is an alternative approach to adding managed code to the generated proxy class.

    -

    17.5.5 Extending proxy classes with additional C# code

    +

    17.5.6 Extending proxy classes with additional C# code

    diff --git a/Examples/test-suite/csharp/csharp_typemaps_runme.cs b/Examples/test-suite/csharp/csharp_typemaps_runme.cs index 42f2fe8d1..846644f09 100644 --- a/Examples/test-suite/csharp/csharp_typemaps_runme.cs +++ b/Examples/test-suite/csharp/csharp_typemaps_runme.cs @@ -15,13 +15,16 @@ public class runme initialLetters.Append(myChar); myChar = csharp_typemaps.partyon("off"); initialLetters.Append(myChar); - myChar = csharp_typemaps.STRINGCONSTANT; - initialLetters.Append(myChar); - if (initialLetters.ToString() != "bhox") + if (initialLetters.ToString() != "bho") throw new Exception("initial letters failed"); - if (csharp_typemaps.go != 'z') - throw new Exception("go variable failed"); + // $csinput expansion + csharp_typemaps.myInt = 1; + try { + csharp_typemaps.myInt = -1; + throw new Exception("oops"); + } catch (ApplicationException) { + } // Eager garbage collector test { diff --git a/Examples/test-suite/csharp_typemaps.i b/Examples/test-suite/csharp_typemaps.i index b982dc964..dcef278f6 100644 --- a/Examples/test-suite/csharp_typemaps.i +++ b/Examples/test-suite/csharp_typemaps.i @@ -22,14 +22,6 @@ return ret; } %} -// test valueparm attribute -%typemap(csvarin, excode=SWIGEXCODE2, valueparm="tempValue") char * %{ - set { - string tempValue = new string(value, 3); - $imcall;$excode - } %} - - %inline %{ namespace Space { class Things { @@ -38,8 +30,6 @@ namespace Space { static char* stop(char *val) { return val; } }; char* partyon(char *val) { return val; } - #define STRINGCONSTANT "xyz string" - char *go = "zap"; } %} @@ -100,3 +90,14 @@ Number times12(const Number* num) { }; %} +// Test $csinput expansion +%typemap(csvarin, excode=SWIGEXCODE2) int %{ + set { + if ($csinput < 0) + throw new ApplicationException("number too small!"); + $imcall;$excode + } %} + +%inline %{ +int myInt = 0; +%} diff --git a/Source/Modules/csharp.cxx b/Source/Modules/csharp.cxx index 640050e2b..79761cdba 100644 --- a/Source/Modules/csharp.cxx +++ b/Source/Modules/csharp.cxx @@ -2652,6 +2652,7 @@ public: SwigType *pt = Getattr(p, "type"); if ((tm = Getattr(p, "tmap:csvarin"))) { substituteClassname(pt, tm); + Replaceall(tm, "$csinput", "value"); Replaceall(tm, "$imcall", imcall); excodeSubstitute(n, tm, "csvarin", p); Printf(module_class_code, "%s", tm); @@ -2869,8 +2870,7 @@ public: String *pn = Getattr(p, "name"); if (setter) { // Note that in C# properties, the input variable name is always called 'value' - String *valueparm = Getattr(p, "tmap:csvarin:valueparm"); - arg = valueparm ? Copy(valueparm) : NewString("value"); + arg = NewString("value"); } else { // Use C parameter name unless it is a duplicate or an empty parameter name int count = 0;