//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Wrappers to turn various Source Utl* containers into CLR // enumerables. // // $NoKeywords: $ //=============================================================================// #ifndef UTLCONTAINER_CLI_H #define UTLCONTAINER_CLI_H #if defined( _WIN32 ) #pragma once #endif /// Handy wrapper to turn any indexing property into /// an IList (assuming it is numbered 0..count). You /// need only initialize it with a delegate that yields /// int->type and another that gets the count. generic< typename T > public ref class IndexPropertyToIListReadOnly : public System::Collections::Generic::IList { public: delegate T lGetter( int idx ); // will wrap the indexer delegate int lCounter( ); // will wrap the counter IndexPropertyToIListReadOnly( lGetter ^getter, lCounter ^counter ) { m_getterFunc = getter; m_counterFunc = counter; } /* virtual ~IndexPropertyToIListReadOnly( ) { !IndexPropertyToIListReadOnly( ); } virtual !IndexPropertyToIListReadOnly( ) {} */ property int Count { virtual int get() { return m_counterFunc(); } } #pragma region IList Members virtual int IndexOf(T item) { int count = Count::get(); for ( int i = 0 ; i < count ; ++i ) { if ( item->Equals( m_getterFunc(i) ) ) return i; } return -1; } virtual void Insert(int index, T item) { throw gcnew NotSupportedException( "Read-only." ); } virtual void RemoveAt(int index) { throw gcnew NotSupportedException("Read-only."); } property T default[int] { virtual T get(int index) { if ( index < 0 || index > Count::get() ) { throw gcnew ArgumentOutOfRangeException(); } else { return m_getterFunc(index); } } virtual void set(int index, T to) { throw gcnew NotSupportedException("Read-only."); } } #pragma endregion #pragma region ICollection Members virtual void Add(T item) { throw gcnew NotSupportedException("Read-only."); } virtual void Clear() { throw gcnew NotSupportedException("Read-only."); } virtual bool Contains(T item) { return IndexOf(item) != -1; } virtual void CopyTo( cli::array ^arr, int start) { int stop = Count::get(); for (int i = 0 ; i < stop ; ++i ) { arr->SetValue((*this)[i],start+i); } // throw gcnew NotImplementedException(); } property bool IsReadOnly { virtual bool get() { return true; } } virtual bool Remove(T item) { throw gcnew NotSupportedException("Read-only."); } #pragma endregion #pragma region Enumerator ref class LinearEnumerator : System::Collections::Generic::IEnumerator { // Enumerators are positioned before the first element // until the first MoveNext() call. int position; public: LinearEnumerator(IndexPropertyToIListReadOnly ^owner) { m_owner = owner; position = -1; } ~LinearEnumerator(){}; !LinearEnumerator(){}; virtual bool MoveNext() { position++; return ( position < m_owner->Count ); } virtual void Reset() { position = -1; } virtual property T Current { virtual T get() = System::Collections::Generic::IEnumerator::Current::get { if ( position >= 0 && position < m_owner->Count ) { return m_owner[position]; } else { throw gcnew InvalidOperationException(); } } } virtual property System::Object^ CurrentAgainBecauseCPP_CLISucks { virtual System::Object^ get() = System::Collections::IEnumerator::Current::get { if ( position >= 0 && position < m_owner->Count ) { return m_owner[position]; } else { throw gcnew InvalidOperationException(); } } } IndexPropertyToIListReadOnly ^m_owner; }; #pragma endregion #pragma region IEnumerable Members virtual System::Collections::Generic::IEnumerator ^ GetEnumerator() { return gcnew LinearEnumerator(this); } virtual System::Collections::IEnumerator^ GetEnumerator2() = System::Collections::IEnumerable::GetEnumerator { return gcnew LinearEnumerator(this); } #pragma endregion protected: lGetter ^m_getterFunc; lCounter ^m_counterFunc; }; #if 0 /// /// Tiny class that wraps an indexing property in a class with an IList interface /// so that the WPF databinding can access it. /// /// /// Assumes that all indexes are from 0..count. /// /// The type of the class whose property we wrap /// The type of the value returned from the class property public class BindingWrapper : IList, INotifyCollectionChanged { // lambda types. You'll pass in one of each of these with the constructor. /// /// Given an int, return the i-th element of wrapper property in owning class. /// public delegate T lGetter( int idx ); /// /// Given an int, return the i-th element of wrapper property in owning class. /// public delegate int lCounter( ); public BindingWrapper( /*U owner,*/ lGetter getter, lCounter counter ) { // m_owner = owner; m_getterFunc = getter; m_counterFunc = counter; } #region IList Members public int IndexOf(T item) { throw new NotImplementedException(); /* // hang onto this number int count = Count; for (int i = 0 ; i < count ; ++i ) { if (this[i] == item) return i; } return -1; */ } public void Insert(int index, T item) { throw new NotSupportedException( "Read-only." ); } public void RemoveAt(int index) { throw new NotSupportedException("Read-only."); } public T this[int index] { get { if (index < 0 || index > Count) { throw new ArgumentOutOfRangeException(); } else { return m_getterFunc(index); } } set { throw new NotSupportedException("Read-only."); } } #endregion #region ICollection Members public void Add(T item) { throw new NotSupportedException("Read-only."); } public void Clear() { throw new NotSupportedException("Read-only."); } public bool Contains(T item) { throw new NotImplementedException(); } public void CopyTo(T[] array, int arrayIndex) { throw new NotSupportedException("Noncopyable."); } public int Count { get { return m_counterFunc(); } } public bool IsReadOnly { get { return true; } } public bool Remove(T item) { throw new NotSupportedException("Read-only."); } #endregion #region Enumerator public class LinearEnumerator : System.Collections.IEnumerator { // Enumerators are positioned before the first element // until the first MoveNext() call. int position = -1; public LinearEnumerator(BindingWrapper owner) { m_owner = owner; } public bool MoveNext() { position++; return ( position < m_owner.Count ); } public void Reset() { position = -1; } public Object Current { get { try { return m_owner[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } BindingWrapper m_owner; } #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { throw new NotImplementedException(); // return new LinearEnumerator(this); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new LinearEnumerator(this); } #endregion #region INotifyCollectionChanged public event NotifyCollectionChangedEventHandler CollectionChanged; public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (CollectionChanged != null) { CollectionChanged(this, e); } } #endregion #region Private data // U m_owner; lGetter m_getterFunc; lCounter m_counterFunc; #endregion } #endif /// Common code for classes that wrap native RR /// types. You have the option to make a COPY /// rather than a pointer to the native class: /// in that case, the constructor here new's a copy /// of the given type on the heap, and the finalizer /// deletes it. /// It may be necessary to specialize the constructor /// in certain cases where a managed function is not /// allowed to new the given type. /// Subclass this with a CLI version of the wrapped /// class and create whatever accessor properties you /// would like to expose to the managed side. template< typename T > public ref class NativeTypeCopyWrapper { public: NativeTypeCopyWrapper( ) : m_pNative(NULL), m_bIsCopy(true) {}; NativeTypeCopyWrapper( T* from ) // this overload assumes no copy { m_bIsCopy = false; if ( false ) { m_pNative = new T( *from ); } else { m_pNative = from; } } NativeTypeCopyWrapper( T* from, bool bCopy ) { m_bIsCopy = bCopy; if ( bCopy ) { m_pNative = new T( *from ); } else { m_pNative = from; } } ~NativeTypeCopyWrapper() { this->!NativeTypeCopyWrapper(); } !NativeTypeCopyWrapper() { if ( m_bIsCopy ) delete m_pNative; m_pNative = NULL; } // copy constructor NativeTypeCopyWrapper(NativeTypeCopyWrapper% r) { m_bIsCopy = r->m_bIsCopy; if (m_bIsCopy) m_pNative = new T( *r->GetNativePtr() ); else m_pNative = r->GetNativePtr(); } inline bool IsValid( ) { return m_pNative != NULL; } /// use the assignment operator on the internal native type, copying /// over the one in the given FROM class, and make me point to it. /// So named to be explicit about what's happening, and because I'm /// not sure what happens when you = on a ref. void Assign( NativeTypeCopyWrapper^ from ) { *m_pNative = *from.m_pNative; } /// please be careful T* GetNativePtr() { return m_pNative; } protected: T* m_pNative; bool m_bIsCopy; }; // CUtlDict as enumerable. #include "utldict.h" /* template class CUtlDict */ namespace Tier1CLI { namespace Containers { public interface class INotifiableList : public System::Collections::IList , public System::Collections::Specialized::INotifyCollectionChanged { virtual void OnCollectionChanged(System::Collections::Specialized::NotifyCollectionChangedEventArgs ^e) = 0; }; } }; /// /// A class that wraps a tier1 CUtlDict in a way that can be iterated over from C#. /// /// /// The CONVERTOR template parameter below shall be the class /// that wraps the native C++ * type into a CLR reference type. /// It should have a constructor that takes a parameter of type /// T* , which will be called like: /// return gcnew CONVERTOR(T *foo); /// template > public ref class CCLIUtlDictEnumerable : // public System::Collections::IEnumerable, public Tier1CLI::Containers::INotifiableList { public: typedef CUtlDict< T,I > dict_t; typedef CONVERTOR value_t; typedef const char * key_t; #pragma region Enumeration interface ref struct tuple_t { key_t key; value_t ^val; I index; inline tuple_t( const key_t &k, T &v, I i ) : key(k), index(i) { val = gcnew CONVERTOR( &v, i ); }; // conversions to CLR types property System::String^ Name { System::String^ get() { return gcnew String(key); } } property CONVERTOR^ Val { CONVERTOR^ get() { return val; } } property I Index { I get() { return index; } } }; /* // convenience functions for WPF databindings, which may hand back a // tuple_t object to C#, which can't get at the typedefs above. // this can cause trouble with getting at the properties; rather than // use reflection, you can just use these. System::String^ NameFor( tuple_t ^ tup ) { return tup->Name; } value_t^ ValFor( tuple_t ^ tup ) { return tup->Val; } */ CONVERTOR ^GetValue( I idx ) { return gcnew CONVERTOR(m_pInnerDict->Element(idx)); } CONVERTOR ^GetKey( I idx ) { return gcnew String(m_pInnerDict->GetElementName(idx)); } property int Count { virtual int get() { return m_pInnerDict->Count(); } } /// Iterator type. Walks over the UtlDict in the same order as its /// internal iterator. Returns a tuple of , where key is /// always a string type (as in the utldict). /// TODO: can I make this a value struct so it doesn't need to be /// boxed/unboxed? ref struct Enumerator : public System::Collections::IEnumerator { typedef CCLIUtlDictEnumerable owner_t; Enumerator( dict_t *pDict ) : m_pEnumeratedDict(pDict), m_bIsBefore(true) { m_index = dict_t::InvalidIndex(); } inline bool IsValid() { return m_pEnumeratedDict->IsValidIndex(m_index); } // IEnumerator interface virtual void Reset() { m_bIsBefore = true; m_index = m_pEnumeratedDict->First(); } // return false when falling off the end virtual bool MoveNext() { if (m_bIsBefore) { m_bIsBefore = false; m_index = m_pEnumeratedDict->First(); return IsValid(); } else { if ( !IsValid() ) { return false; } else { m_index = m_pEnumeratedDict->Next( m_index ); return IsValid(); } } } property System::Object^ Current { virtual System::Object^ get() { if ( IsValid() ) { return gcnew tuple_t( m_pEnumeratedDict->GetElementName(m_index), m_pEnumeratedDict->Element(m_index), m_index); } else { throw gcnew System::InvalidOperationException(); } } }; // data: protected: I m_index; dict_t *m_pEnumeratedDict; bool m_bIsBefore; }; virtual System::Collections::IEnumerator^ GetEnumerator() { return gcnew Enumerator(m_pInnerDict); } #pragma endregion bool IsValidIndex( I idx ) { return m_pInnerDict->IsValidIndex(idx); } tuple_t ^GetElement( I i ) { return gcnew tuple_t( m_pInnerDict->GetElementName(i), m_pInnerDict->Element(i), i); } #pragma region ILIST interface virtual int IndexOf( System::Object ^obj ) { tuple_t ^t = dynamic_cast< tuple_t ^>(obj); if (t) { return t->index; } else { throw gcnew System::ArrayTypeMismatchException(); } } virtual bool Contains( System::Object ^obj ) { tuple_t ^t = dynamic_cast< tuple_t ^>(obj); if (t) { return IsValidIndex(t->index); } else { throw gcnew System::ArrayTypeMismatchException(); } } virtual void Insert(int index, System::Object ^ item) { throw gcnew NotSupportedException( "Read-only." ); } virtual void Remove(System::Object ^) { throw gcnew NotSupportedException("Read-only."); } virtual void RemoveAt(int index) { throw gcnew NotSupportedException("Read-only."); } virtual int Add(System::Object ^o) { throw gcnew NotSupportedException("Read-only."); } virtual void Clear() { throw gcnew NotSupportedException("Read-only."); } virtual property Object ^ SyncRoot { Object ^ get() { return this; } } virtual void CopyTo(Array ^arr, int start) { int stop = Count::get(); for (int i = 0 ; i < stop ; ++i ) { arr->SetValue((*this)[i],start+i); } // throw gcnew NotImplementedException(); } property System::Object ^ default[int] { virtual System::Object ^get( int index ) { if (index < 0 || index > Count) { throw gcnew ArgumentOutOfRangeException(); } else { return GetElement(index); } } virtual void set(int idx, System::Object ^s) { throw gcnew NotSupportedException("Read-only."); } } property bool IsReadOnly { virtual bool get() { return true; } } property bool IsFixedSize { virtual bool get() { return true; } } property bool IsSynchronized { virtual bool get() { return true; } } #pragma endregion #pragma region INotifyCollectionChanged interface System::Collections::Specialized::NotifyCollectionChangedEventHandler ^m_CollectionChanged; virtual event System::Collections::Specialized::NotifyCollectionChangedEventHandler ^CollectionChanged { void add(System::Collections::Specialized::NotifyCollectionChangedEventHandler ^ d) { m_CollectionChanged += d; } void remove(System::Collections::Specialized::NotifyCollectionChangedEventHandler ^ d) { m_CollectionChanged -= d; } } virtual void OnCollectionChanged(System::Collections::Specialized::NotifyCollectionChangedEventArgs ^e) { if (m_CollectionChanged != nullptr) { m_CollectionChanged(this, e); } } #pragma endregion /// construct with a pointer at a UtlDict. Does not /// own the pointer; simply stores it internally. CCLIUtlDictEnumerable( dict_t *pInnerDict ) //: m_CollectionChanged(gcnew System::Collections::Specialized::NotifyCollectionChangedEventHandler ) { Init(pInnerDict); }; CCLIUtlDictEnumerable( ) :m_pInnerDict(NULL)// //,m_CollectionChanged(gcnew System::Collections::Specialized::NotifyCollectionChangedEventHandler ) { }; void Init( dict_t *pInnerDict ) { m_pInnerDict = pInnerDict; } protected: dict_t *m_pInnerDict; }; #endif