From f0067b6bbfd1e43f01016a78428bc3d55434bc39 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 7 Mar 2019 02:25:49 +0100 Subject: [PATCH] Add std::set<> typemaps for C# Create new Lib/csharp/std_set.i based on the existing std_map.i and run li_std_set unit test for C# as well. Notice that the set operations defined by the base ISet<> interface are not implemented yet. --- CHANGES.current | 6 + .../test-suite/csharp/li_std_set_runme.cs | 55 ++++ Examples/test-suite/li_std_set.i | 6 +- Lib/csharp/std_set.i | 239 ++++++++++++++++++ 4 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 Examples/test-suite/csharp/li_std_set_runme.cs create mode 100644 Lib/csharp/std_set.i diff --git a/CHANGES.current b/CHANGES.current index 80230e964..3e3da1099 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,12 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.0.0 (in progress) =========================== +2019-03-12: vadz + [C#] Add std::set<> typemaps. + + Not all methods of System.Collections.Generic.ISet are implemented yet, but all the + basic methods, including elements access and iteration, are. + 2019-03-11: dirteat,opoplawski [Octave] Fix compilation errors in Octave 5.1. diff --git a/Examples/test-suite/csharp/li_std_set_runme.cs b/Examples/test-suite/csharp/li_std_set_runme.cs new file mode 100644 index 000000000..c186c2f7a --- /dev/null +++ b/Examples/test-suite/csharp/li_std_set_runme.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using li_std_setNamespace; + +public class runme +{ + static void checkThat(bool mustBeTrue, string message) + { + if (!mustBeTrue) + throw new Exception("Test that the set " + message + " failed"); + } + + static void Main() + { + StringSet ss = new StringSet(); + + // Check the interface methods first. + ISet s = ss; + + checkThat(s.Count == 0, "is initially empty"); + checkThat(!s.Contains("key"), "doesn't contain inexistent element"); + checkThat(!s.Remove("key"), "returns false when removing inexistent element"); + + checkThat(s.Add("key"), "returns true when adding a new element"); + checkThat(!s.Add("key"), "returns false when adding an existing element"); + checkThat(s.Contains("key"), "contains the just added element"); + checkThat(s.Remove("key"), "returns true when removing an existing element"); + checkThat(s.Count == 0, "is empty again"); + + checkThat(s.Add("key1"), "Add(key1) returns true"); + checkThat(s.Add("key2"), "Add(key2) returns true"); + checkThat(s.Add("key3"), "Add(key3) returns true"); + + // Also check a different interface, providing a different Add() (sic!). + ICollection coll = ss; + coll.Add("key"); + checkThat(ss.Count == 4, "contains 4 elements"); + + // Now use object-specific methods, mimicking HashSet<>. + string val; + checkThat(ss.TryGetValue("key1", out val), "could retrieve existing item"); + checkThat(val.Equals("key1"), "value was returned correctly by TryGetValue()"); + checkThat(!ss.TryGetValue("no-such-key", out val), "couldn't retrieve inexistent item"); + checkThat(val == null, "value was reset after failed TryGetValue()"); + + IList list = new List(); + foreach (string str in ss) { + list.Add(str); + } + checkThat(list.Count == 4, "copy contains 4 elements"); + + ss.Clear(); + checkThat(ss.Count == 0, "is empty after Clear()"); + } +} diff --git a/Examples/test-suite/li_std_set.i b/Examples/test-suite/li_std_set.i index fc9db42a9..bb952cd85 100644 --- a/Examples/test-suite/li_std_set.i +++ b/Examples/test-suite/li_std_set.i @@ -15,15 +15,15 @@ %include %include -// Use language macros since Java doesn't have multiset support (yet) +// Use language macros since Java and C# don't have multiset support (yet) // and uses different naming conventions. #if defined(SWIGRUBY) || defined(SWIGPYTHON) %include %template(set_int) std::multiset; %template(v_int) std::vector; %template(set_string) std::set; -#elif defined(SWIGJAVA) - %template(StringSet) std::set; +#elif defined(SWIGJAVA) || defined(SWIGCSHARP) + %template(StringSet) std::set; #endif #if defined(SWIGRUBY) diff --git a/Lib/csharp/std_set.i b/Lib/csharp/std_set.i new file mode 100644 index 000000000..b988d8d91 --- /dev/null +++ b/Lib/csharp/std_set.i @@ -0,0 +1,239 @@ +/* ----------------------------------------------------------------------------- + * std_map.i + * + * SWIG typemaps for std::set + * + * The C# wrapper is made to look and feel like a C# System.Collections.Generic.HashSet<>. + * ----------------------------------------------------------------------------- */ + +%{ +#include +#include +#include +%} + +%csmethodmodifiers std::set::size "private" +%csmethodmodifiers std::set::getitem "private" +%csmethodmodifiers std::set::create_iterator_begin "private" +%csmethodmodifiers std::set::get_next "private" +%csmethodmodifiers std::set::destroy_iterator "private" + +namespace std { + +// TODO: Add support for comparator and allocator template parameters. +template +class set { + +%typemap(csinterfaces) std::set "global::System.IDisposable, global::System.Collections.Generic.ISet<$typemap(cstype, T)>\n"; +%proxycode %{ + void global::System.Collections.Generic.ICollection<$typemap(cstype, T)>.Add(string item) { + ((global::System.Collections.Generic.ISet<$typemap(cstype, T)>)this).Add(item); + } + + public bool TryGetValue($typemap(cstype, T) equalValue, out $typemap(cstype, T) actualValue) { + try { + actualValue = getitem(equalValue); + return true; + } catch { + actualValue = default($typemap(cstype, T)); + return false; + } + } + + public int Count { + get { + return (int)size(); + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public void CopyTo($typemap(cstype, T)[] array) { + CopyTo(array, 0); + } + + public void CopyTo($typemap(cstype, T)[] array, int arrayIndex) { + if (array == null) + throw new global::System.ArgumentNullException("array"); + if (arrayIndex < 0) + throw new global::System.ArgumentOutOfRangeException("arrayIndex", "Value is less than zero"); + if (array.Rank > 1) + throw new global::System.ArgumentException("Multi dimensional array.", "array"); + if (arrayIndex+this.Count > array.Length) + throw new global::System.ArgumentException("Number of elements to copy is too large."); + + foreach ($typemap(cstype, T) item in this) { + array.SetValue(item, arrayIndex++); + } + } + + public void ExceptWith(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public void IntersectWith(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool IsProperSubsetOf(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool IsProperSupersetOf(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool IsSubsetOf(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool IsSupersetOf(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool Overlaps(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public bool SetEquals(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public void SymmetricExceptWith(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + public void UnionWith(global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)> other) { throw new global::System.Exception("TODO"); } + + private global::System.Collections.Generic.ICollection<$typemap(cstype, T)> Items { + get { + global::System.Collections.Generic.ICollection<$typemap(cstype, T)> items = new global::System.Collections.Generic.List<$typemap(cstype, T)>(); + int size = this.Count; + if (size > 0) { + global::System.IntPtr iter = create_iterator_begin(); + for (int i = 0; i < size; i++) { + items.Add(get_next(iter)); + } + destroy_iterator(iter); + } + return items; + } + } + + global::System.Collections.Generic.IEnumerator<$typemap(cstype, T)> global::System.Collections.Generic.IEnumerable<$typemap(cstype, T)>.GetEnumerator() { + return new $csclassnameEnumerator(this); + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() { + return new $csclassnameEnumerator(this); + } + + public $csclassnameEnumerator GetEnumerator() { + return new $csclassnameEnumerator(this); + } + + // Type-safe enumerator + /// Note that the IEnumerator documentation requires an InvalidOperationException to be thrown + /// 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 : global::System.Collections.IEnumerator, + global::System.Collections.Generic.IEnumerator<$typemap(cstype, T)> + { + private $csclassname collectionRef; + private global::System.Collections.Generic.IList<$typemap(cstype, T)> ItemsCollection; + private int currentIndex; + private $typemap(cstype, T) currentObject; + private int currentSize; + + public $csclassnameEnumerator($csclassname collection) { + collectionRef = collection; + ItemsCollection = new global::System.Collections.Generic.List<$typemap(cstype, T)>(collection.Items); + currentIndex = -1; + currentObject = null; + currentSize = collectionRef.Count; + } + + // Type-safe iterator Current + public $typemap(cstype, T) Current { + get { + if (currentIndex == -1) + throw new global::System.InvalidOperationException("Enumeration not started."); + if (currentIndex > currentSize - 1) + throw new global::System.InvalidOperationException("Enumeration finished."); + if (currentObject == null) + throw new global::System.InvalidOperationException("Collection modified."); + return currentObject; + } + } + + // Type-unsafe IEnumerator.Current + object global::System.Collections.IEnumerator.Current { + get { + return Current; + } + } + + public bool MoveNext() { + int size = collectionRef.Count; + bool moveOkay = (currentIndex+1 < size) && (size == currentSize); + if (moveOkay) { + currentIndex++; + currentObject = ItemsCollection[currentIndex]; + } else { + currentObject = null; + } + return moveOkay; + } + + public void Reset() { + currentIndex = -1; + currentObject = null; + if (collectionRef.Count != currentSize) { + throw new global::System.InvalidOperationException("Collection modified."); + } + } + + public void Dispose() { + currentIndex = -1; + currentObject = null; + } + } + +%} + + public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T key_type; + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + + set(); + set(const set& other); + size_type size() const; + bool empty() const; + %rename(Clear) clear; + void clear(); + %extend { + bool Add(const value_type& item) { + return $self->insert(item).second; + } + + bool Contains(const value_type& item) { + return $self->count(item) != 0; + } + + bool Remove(const value_type& item) { + return $self->erase(item) != 0; + } + + const value_type& getitem(const value_type& item) throw (std::out_of_range) { + std::set::iterator iter = $self->find(item); + if (iter == $self->end()) + throw std::out_of_range("item not found"); + + return *iter; + } + + // create_iterator_begin(), get_next() and destroy_iterator work together to provide a collection of items to C# + %apply void *VOID_INT_PTR { std::set::iterator *create_iterator_begin } + %apply void *VOID_INT_PTR { std::set::iterator *swigiterator } + + std::set::iterator *create_iterator_begin() { + return new std::set::iterator($self->begin()); + } + + const key_type& get_next(std::set::iterator *swigiterator) { + std::set::iterator iter = *swigiterator; + (*swigiterator)++; + return *iter; + } + + void destroy_iterator(std::set::iterator *swigiterator) { + delete swigiterator; + } + } +}; + +}