PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++.NET]Ersatz für STL-Container?


Gast
2006-08-01, 15:45:42
Hi Leute,

ich arbeite daran, ein Programm nach .NET zu portieren, in dem Gebrauch von Container-Klassen aus der STL, insbesondere vector, deque und list, wird. Das Problem ist, daß sich diese nicht mit managed Datentypen vertragen - eine managed Klasse darf keine STL-Container als Member enthalten, umgekehrt ist als Template-Typ eines Containers kein managed Typ zugelassen:

ref class X
{
//...
std::vector <double> m_container; // ist nicht erlaubt
};

ref class Y;
std::vector<Y> container; // ist ebenfalls nicht erlaubt


Ich werde die STL-Sachen wohl also rausschmeißen müssen. Aber was kann ich als Ersatz nehmen? C++.NET enthält zwar eine std::vector-ähnliche array-Klasse, deren Funktionalität aber sehr viel eingeschränkter ist, z.B. scheint es keine push_back-Entsprechung zu geben. Das Anhängen neuer Elemente ist daher recht aufwändig:

// STL-Vector:
std::vector<double> vec;
//...
vec.push_back(5.0);

// .NET-array:
array<double> vec;
//...
int vec_back = vec.Length;
System::Array::Resize(vec, vec_Back+1);
vec[vec_back] = 5.0;

Gibt es keine komfortabler zu handhabenden Array-Klassen die .NET-kompatibel sind?

Gast
2006-08-01, 16:20:29
Wieso nimmest Du nicht System.Collections.Generic.List<T>?

Gast
2006-08-01, 16:26:37
weil ich bis gerade eben nichts von deren Existenz wußte?

Auch in der MSDN und auf diversen Webseiten habe ich zu STL vs. .NET so gut wie nichts gefunden.

Gast
2006-08-01, 16:28:24
weil ich bis gerade eben nichts von deren Existenz wußte?

Dann entschuldige vielmals für den gutgemeinten Hinweis...

Gast
2006-08-01, 17:04:41
die Klasse System::Collections::Generic::Queue scheint ja auf den ersten Blick ein brauchbarer Ersatz für std::deque zu sein. Nur leider fehlen der bis auf push_back(-> Enqueue) und pop_front (-> Dequeue) mal wieder alle wichtigen Member, z.B. das Auslesen eines Elements an einer bestimmten Stelle (außer am Anfang). Dazu muß man das erst umständlich in ein Array kopieren.

Kabelsalat
2006-08-01, 23:49:44
Da es eine Queue ist, wird der Direktzugriff mittels Index auch nicht benötigt. Du solltest wie bereits angemerkt eine Liste verwenden...

Gast
2006-08-02, 09:52:49
Da es eine Queue ist, wird der Direktzugriff mittels Index auch nicht benötigt. schon interessant, daß du besser weißt als ich was ich benötige.

Aber um deine These mal direkt an einem konkreten Beispiel zu widerlegen:
mein Programm nimmt Meßwerte von einem externen Meßgerät auf und stellt sie grafisch dar. Da nur eine begrenzte Zahl an Meßwerten dargestellt werden soll (z.B. 1000), die Messung aber unbegrenzt weiterlaufen soll, ist es notwendig, ältere Meßwerte rauswerfen zu können (z.B. Meßwert Nr. 1 wenn Meßwert Nr. 1001 aufgenommen wird usw.). Dazu bietet sich an, die Meßwerte in einer Queue zu speichern: den aktuellsten Wert hängt man hinten an, den ältesten wirft man vorne raus:

std::deque<double> MeasValues;

void AddNewMeasVal(double newVal)
{
MeasValues.push_back(newVal);
// ist Queue voll? Dann ältesten Wert verwerfen:
if (MeasValues.size() > 1000)
MeasValues.pop_front();
}

Da aber die Meßwerte nicht nur in der Queue rumliegen sollen, sondern z.B. auch grafisch dargestellt werden sollen, müssen sie über Index ansprechbar sein.

Ok, für die grafische Darstellung würde mal wohl die Konvertierung zu einem Array (wie mit Queue::ToArray) noch hinnehmen können, aber das Ansprechen der einzelnen Elemente wird noch für andere Zwecke benötigt, etwa für mathematischen Operationen wie der Berechnung des Integrals der von den Meßwerten gebildeten Kurve oder des Mittelwertes und der Standardabweichung.

Du solltest wie bereits angemerkt eine Liste verwenden...der fehlt eine Funktion zum Entfernen des ersten Elements (pop_front bei std::deque, Dequeue bei Queue). Man könnte zwar RemoveAt(0) benutzen, aber das ist sehr zeitaufwändig (O(Count)).

Trap
2006-08-02, 10:22:21
Für einen Puffer mit fester größe benutzt man sinnvollerweise einen Ringpuffer. Nur wenn der Puffer während des Betriebs wachsen und wieder schrumpfen können soll braucht man sowas wie eine deque.

Ich find die .NET Collections auch nicht so toll...

Kabelsalat
2006-08-02, 10:28:33
Queue in .Net ist nunmal eine &quot;Schlange&quot; bei der du nur an den enden operieren kannst - egal ob das deiner Aufgabenstellung entspricht oder nicht. Wenn du direkten Zugriff haben willst, musst du dir wohl etwas überlegen: -> List (mit bereits angemerktem Haken) -> Dictionary (zwar schnell, aber afaik etwas höherer Speicherbedarf - lohnt erst ab großem n) PS: Sorry für die fehlenden Leerzeilen, die übernimmt er seit dem Forenupdate komischerweise nicht mehr.

Gast
2006-08-02, 12:16:57
Für einen Puffer mit fester größe benutzt man sinnvollerweise einen Ringpuffer. Nur wenn der Puffer während des Betriebs wachsen und wieder schrumpfen können soll braucht man sowas wie eine deque.ganz so fest ist die Puffergröße ja nicht. Was aus meinem Beispiel nicht hervorgeht, weil ich es nicht für wichtig zu erwähnen hielt, ist daß sich die Zahl der Meßwerte im Puffer aus der Zahl in einem vorgegebenen Zeitintervall [t1, t2 = t1 + Delta_t] ergibt. Wird zur Zeit t ein neuer Meßwert aufgenommen, wird t2 auf t und t1 auf t-Delta_t verschoben.
Die Puffergröße steht daher nicht von vorneherein fest und kann variieren.

Trap
2006-08-02, 12:53:39
ganz so fest ist die Puffergröße ja nicht. Was aus meinem Beispiel nicht hervorgeht, weil ich es nicht für wichtig zu erwähnen hielt, ist daß sich die Zahl der Meßwerte im Puffer aus der Zahl in einem vorgegebenen Zeitintervall [t1, t2 = t1 + Delta_t] ergibt. Wird zur Zeit t ein neuer Meßwert aufgenommen, wird t2 auf t und t1 auf t-Delta_t verschoben.
Ich versteh die Erklärung nicht, liegt das an mir oder an der Erklärung?

Ist Delta_t fest? Ist der zeitliche Abstand zwischen den Messwerten fest? Dann kann man einen Ringpuffer benutzen. Ein Array nehmen und einen int pos der die aktuelle Position speichert, wenn du einen Messwert bekommst machst du pos=(pos+1)%ar.size();ar[pos]=wert; und das wars.

Kabelsalat
2006-08-17, 22:53:54
Falls du immer noch an einer entsprechenden Lösung interessiert bist: Folgende Utility Sammlung enthält eine RandomAccessQueue: http://www.yoda.arachsys.com/csharp/miscutil/index.html

Gast
2006-08-18, 01:31:31
der fehlt eine Funktion zum Entfernen des ersten Elements (pop_front bei std::deque, Dequeue bei Queue). Man könnte zwar RemoveAt(0) benutzen, aber das ist sehr zeitaufwändig (O(Count)).

Man könnte auch LinkedList<T> nehmen; LinkedList<T>::RemoveFirst ist O(1) - dafür ist random access aber O(n) (außer First und Last).

Elemental
2006-08-18, 12:05:27
Vielleicht sind die PowerCollections das richtige für dich?
Da gibt es eine Deque-Klasse. Hier mal aus der Doku:


Deque
Double-ended queue. Like a queue and stack combined. Allows adding or removing at both ends in fast
constant time. Also allows indexing and implements IList. Implemented with an array managed as a ring
buffer. The array is grown or shrinks as necessary.


Gibts hier: http://www.wintellect.com/

Musst dich zwar anmelden auf der Seite, auf PowerCollections ist umsonst und OpenSource.

Was es sonst noch gibt in den PowerCollections:
Klassen:


Algorithms:
Algorithms contains a number of static methods that implement algorithms that work on collections. Most of the methods deal with the standard generic collection interfaces such as IEnumerable<T>, ICollection<T> and IList<T>.

Bag<T>:
Bag<T> is a collection that contains items of type T. Unlike a Set, duplicate items (items that compare equal to each other) are allowed in an Bag.

BigList<T>:
BigList<T> provides a list of items, in order, with indices of the items ranging from 0 to one less than the count of items in the collection. BigList<T> is optimized for efficient operations on large (>100 items) lists, especially for insertions, deletions, copies, and concatinations.

CollectionBase<T>:
CollectionBase is a base class that can be used to more easily implement the generic ICollection<T> and non-generic ICollection interfaces.

Deque<T>:
The Deque class implements a type of list known as a Double Ended Queue. A Deque is quite similar to a List, in that items have indices (starting at 0), and the item at any index can be efficiently retrieved. The difference between a List and a Deque lies in the efficiency of inserting elements at the beginning. In a List, items can be efficiently added to the end, but inserting an item at the beginning of the List is slow, taking time proportional to the size of the List. In a Deque, items can be added to the beginning or end equally efficiently, regardless of the number of items in the Deque. As a trade-off for this increased flexibility, Deque is somewhat slower than List (but still constant time) when being indexed to get or retrieve elements.

DictionaryBase<TKey,TValue>:
DictionaryBase is a base class that can be used to more easily implement the generic IDictionary<T> and non-generic IDictionary interfaces.

ListBase<T>:
ListBase is an abstract class that can be used as a base class for a read-write collection that needs to implement the generic IList<T> and non-generic IList collections. The derived class needs to override the following methods: Count, Clear, Insert, RemoveAt, and the indexer. The implementation of all the other methods in IList<T> and IList are handled by ListBase.

MultiDictionary<TKey,TValue>:
The MultiDictionary class that associates values with a key. Unlike an Dictionary, each key can have multiple values associated with it. When indexing an MultiDictionary, instead of a single value associated with a key, you retrieve an enumeration of values.

When constructed, you can chose to allow the same value to be associated with a key multiple times, or only one time.

MultiDictionaryBase<TKey,TValue>:
MultiDictionaryBase is a base class that can be used to more easily implement a class that associates multiple values to a single key. The class implements the generic IDictionary<TKey, ICollection<TValue>> interface.

OrderedBag<T>:
OrderedBag<T> is a collection that contains items of type T. The item are maintained in a sorted order. Unlike a OrderedSet, duplicate items (items that compare equal to each other) are allows in an OrderedBag.

OrderedBag<T>.View:
The OrderedBag<T>.View class is used to look at a subset of the items inside an ordered bag. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods.

OrderedDictionary<TKey,TValue>:
OrderedDictionary<TKey, TValue> is a collection that maps keys of type TKey to values of type TValue. The keys are maintained in a sorted order, and at most one value is permitted for each key.

OrderedDictionary<TKey,TValue>.View:
The OrderedDictionary<TKey,TValue>.View class is used to look at a subset of the keys and values inside an ordered dictionary. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods.

OrderedMultiDictionary<TKey,TValue>:
The OrderedMultiDictionary class that associates values with a key. Unlike an OrderedDictionary, each key can have multiple values associated with it. When indexing an OrderedMultidictionary, instead of a single value associated with a key, you retrieve an enumeration of values.

All of the key are stored in sorted order. Also, the values associated with a given key are kept in sorted order as well.

When constructed, you can chose to allow the same value to be associated with a key multiple times, or only one time.

OrderedMultiDictionary<TKey,TValue>.View:
The OrderedMultiDictionary<TKey,TValue>.View class is used to look at a subset of the keys and values inside an ordered multi-dictionary. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods.

OrderedSet<T>:
OrderedSet<T> is a collection that contains items of type T. The item are maintained in a sorted order, and duplicate items are not allowed. Each item has an index in the set: the smallest item has index 0, the next smallest item has index 1, and so forth.

OrderedSet<T>.View:
The OrderedSet<T>.View class is used to look at a subset of the Items inside an ordered set. It is returned from the Range, RangeTo, RangeFrom, and Reversed methods.

ReadOnlyCollectionBase<T>:
ReadOnlyCollectionBase is a base class that can be used to more easily implement the generic ICollection<T> and non-generic ICollection interfaces for a read-only collection: a collection that does not allow adding or removing elements.

ReadOnlyDictionaryBase<TKey,TValue>:
ReadOnlyDictionaryBase is a base class that can be used to more easily implement the generic IDictionary<T> and non-generic IDictionary interfaces.

ReadOnlyListBase<T>:
ReadOnlyListBase is an abstract class that can be used as a base class for a read-only collection that needs to implement the generic IList<T> and non-generic IList collections. The derived class needs to override the Count property and the get part of the indexer. The implementation of all the other methods in IList<T> and IList are handled by ListBase.

ReadOnlyMultiDictionaryBase<TKey,TValue>
: MultiDictionaryBase is a base class that can be used to more easily implement a class that associates multiple values to a single key. The class implements the generic IDictionary<TKey, ICollection<TValue>> interface. The resulting collection is read-only -- items cannot be added or removed.
Set<T> Set<T> is a collection that contains items of type T. The item are maintained in a haphazard, unpredictable order, and duplicate items are not allowed.



Strukturen:

Pair<TFirst,TSecond>
Stores a pair of objects within a single struct. This struct is useful to use as the T of a collection, or as the TKey or TValue of a dictionary.

Triple<TFirst,TSecond,TThird>
Stores a triple of objects within a single struct. This struct is useful to use as the T of a collection, or as the TKey or TValue of a dictionary.



Delegates:

BinaryPredicate<T>
The BinaryPredicate delegate type encapsulates a method that takes two items of the same type, and returns a boolean value representating some relationship between them. For example, checking whether two items are equal or equivalent is one kind of binary predicate.


mfG