From 6975ecd65c52543bd630730e62a30f42a1ad1bb6 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Wed, 18 Mar 2009 23:30:57 +0000 Subject: [PATCH] std::vector wrapper improvements for .NET 2 implementing IEnumerable git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@11156 626c5289-ae23-0410-ae9c-e8d60b6d4f22 --- CHANGES.current | 10 +++ .../test-suite/csharp/li_std_vector_runme.cs | 34 ++++++++-- Lib/csharp/std_vector.i | 66 +++++++++++++++---- 3 files changed, 93 insertions(+), 17 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index f17090bf5..de41577f8 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -1,6 +1,16 @@ Version 1.3.39 (in progress) ============================ +2008-03-18: wsfulton + [C#] std::vector wrapper improvements for .NET 2 and also providing the + necessary machinery to use the std::vector wrappers with more advanced features such + as LINQ - the C# proxy class now derives from IEnumerable<>. The default is now to + generate code requiring .NET 2 as a minimum, although the C# code can be compiled + for .NET 1 by defining the SWIG_DOTNET_1 C# preprocessor constant. See the + std_vector.i file for more details. + + *** POTENTIAL INCOMPATIBILITY *** + 2008-03-12: wsfulton [Ruby] Fix #2676738 SWIG generated symbol name clashes. diff --git a/Examples/test-suite/csharp/li_std_vector_runme.cs b/Examples/test-suite/csharp/li_std_vector_runme.cs index 718087336..6d6def05d 100644 --- a/Examples/test-suite/csharp/li_std_vector_runme.cs +++ b/Examples/test-suite/csharp/li_std_vector_runme.cs @@ -82,14 +82,17 @@ public class li_std_vector_runme { } catch (ArgumentException) { } } +#if SWIG_DOTNET_1 { + // runtime check that 2D arrays fail double[,] outputarray = new double[collectionSize,collectionSize]; try { vect.CopyTo(outputarray); - throw new Exception("CopyTo (5) test failed"); + throw new Exception("CopyTo (5a) test failed"); } catch (ArgumentException) { } } +#endif { StructVector inputvector = new StructVector(); int arrayLen = 10; @@ -154,6 +157,9 @@ public class li_std_vector_runme { throw new Exception("ICollection constructor null test failed"); } catch (ArgumentNullException) { } + { + myDoubleVector = new DoubleVector() { 123.4, 567.8, 901.2 }; + } // IndexOf() test for (int i=0; i myEnumerator = dv.GetEnumerator(); + while ( myEnumerator.MoveNext() ) { + if (myEnumerator.Current != 77.7) + throw new Exception("Repeat (2) test failed"); + } + } +#endif } { @@ -516,6 +533,13 @@ public class li_std_vector_runme { li_std_vector.halve_in_place(dvec); } + // Dispose() + { + using (StructVector vs = new StructVector() { new Struct(0.0), new Struct(11.1) } ) + using (DoubleVector vd = new DoubleVector() { 0.0, 11.1 } ) { + } + } + // More wrapped methods { RealVector v0 = li_std_vector.vecreal(new RealVector()); diff --git a/Lib/csharp/std_vector.i b/Lib/csharp/std_vector.i index 285581a86..f4ad88bae 100644 --- a/Lib/csharp/std_vector.i +++ b/Lib/csharp/std_vector.i @@ -6,15 +6,22 @@ * * SWIG typemaps for std::vector * C# implementation - * The C# wrapper is made to look and feel like a typesafe C# System.Collections.ArrayList - * All the methods in IList are defined, but we don't derive from IList as this is a typesafe collection. - * Warning: heavy macro usage in this file. Use swig -E to get a sane view on the real file contents! + * The C# wrapper is made to look and feel like a C# System.Collections.Generic.List<> collection. + * For .NET 1 compatibility, define SWIG_DOTNET_1 when compiling the C# code; then the C# wrapper is + * made to look and feel like a typesafe C# System.Collections.ArrayList. All the methods in IList + * are defined, but we don't derive from IList as this is a typesafe collection and the C++ operator== + * must always be defined for the collection type (which it isn't). * * Very often the C# generated code will not compile as the C++ template type is not the same as the C# * proxy type, so use the SWIG_STD_VECTOR_SPECIALIZE or SWIG_STD_VECTOR_SPECIALIZE_MINIMUM macro, eg * * SWIG_STD_VECTOR_SPECIALIZE_MINIMUM(Klass, SomeNamespace::Klass) * %template(VectKlass) std::vector; + * + * Note that IEnumerable<> is implemented in the proxy class which is useful for using LINQ with + * C++ std::vector wrappers. + * + * Warning: heavy macro usage in this file. Use swig -E to get a sane view on the real file contents! * ----------------------------------------------------------------------------- */ // Warning: Use the typemaps here in the expectation that the macros they are in will change name. @@ -25,7 +32,7 @@ // MACRO for use within the std::vector class body // CSTYPE and CTYPE respectively correspond to the types in the cstype and ctype typemaps %define SWIG_STD_VECTOR_MINIMUM_INTERNAL(CONST_REFERENCE_TYPE, CSTYPE, CTYPE...) -%typemap(csinterfaces) std::vector "IDisposable, System.Collections.IEnumerable"; +%typemap(csinterfaces) std::vector "IDisposable, System.Collections.IEnumerable\n#if !SWIG_DOTNET_1\n , System.Collections.Generic.IEnumerable\n#endif\n"; %typemap(cscode) std::vector %{ public $csclassname(System.Collections.ICollection c) : this() { if (c == null) @@ -79,15 +86,30 @@ } } - public void CopyTo(System.Array array) { +#if SWIG_DOTNET_1 + public void CopyTo(System.Array array) +#else + public void CopyTo(CSTYPE[] array) +#endif + { CopyTo(0, array, 0, this.Count); } - public void CopyTo(System.Array array, int arrayIndex) { +#if SWIG_DOTNET_1 + public void CopyTo(System.Array array, int arrayIndex) +#else + public void CopyTo(CSTYPE[] array, int arrayIndex) +#endif + { CopyTo(0, array, arrayIndex, this.Count); } - public void CopyTo(int index, System.Array array, int arrayIndex, int count) { +#if SWIG_DOTNET_1 + public void CopyTo(int index, System.Array array, int arrayIndex, int count) +#else + public void CopyTo(int index, CSTYPE[] array, int arrayIndex, int count) +#endif + { if (array == null) throw new ArgumentNullException("array"); if (index < 0) @@ -97,14 +119,19 @@ if (count < 0) throw new ArgumentOutOfRangeException("count", "Value is less than zero"); if (array.Rank > 1) - throw new ArgumentException("Multi dimensional array."); + throw new ArgumentException("Multi dimensional array.", "array"); if (index+count > this.Count || arrayIndex+count > array.Length) throw new ArgumentException("Number of elements to copy is too large."); for (int i=0; i System.Collections.Generic.IEnumerable.GetEnumerator() { + return new $csclassnameEnumerator(this); + } +#endif + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new $csclassnameEnumerator(this); } @@ -118,7 +145,11 @@ /// whenever the collection is modified. This has been done for changes in the size of the /// collection but not when one of the elements of the collection is modified as it is a bit /// tricky to detect unmanaged code that modifies the collection under our feet. - public sealed class $csclassnameEnumerator : System.Collections.IEnumerator { + public sealed class $csclassnameEnumerator : System.Collections.IEnumerator +#if !SWIG_DOTNET_1 + , System.Collections.Generic.IEnumerator +#endif + { private $csclassname collectionRef; private int currentIndex; private object currentObject; @@ -170,6 +201,13 @@ throw new InvalidOperationException("Collection modified."); } } + +#if !SWIG_DOTNET_1 + public void Dispose() { + currentIndex = -1; + currentObject = null; + } +#endif } %} @@ -293,6 +331,7 @@ SWIG_STD_VECTOR_MINIMUM_INTERNAL(const value_type&, CSTYPE, CTYPE) // Extra methods added to the collection class if operator== is defined for the class being wrapped // CSTYPE and CTYPE respectively correspond to the types in the cstype and ctype typemaps +// The class will then implement IList<>, which adds extra functionality %define SWIG_STD_VECTOR_EXTRA_OP_EQUALS_EQUALS(CSTYPE, CTYPE...) %extend { bool Contains(const value_type& value) { @@ -312,10 +351,13 @@ SWIG_STD_VECTOR_MINIMUM_INTERNAL(const value_type&, CSTYPE, CTYPE) index = (int)(self->rend() - 1 - rit); return index; } - void Remove(const value_type& value) { + bool Remove(const value_type& value) { std::vector::iterator it = std::find(self->begin(), self->end(), value); - if (it != self->end()) + if (it != self->end()) { self->erase(it); + return true; + } + return false; } } %enddef