Ein C ++ Iterator-Adapter, die Packungen und verbirgt eine innere Iterator und wandelt den iterierten Typen
Frage
Nachdem mit diesem toyed ich vermute, dass es nicht aus der Ferne möglich ist, aber ich dachte, dass ich die Experten fragen würde. Ich habe den folgenden C ++ Code:
class IInterface { virtual void SomeMethod() = 0; }; class Object { IInterface* GetInterface() { ... } }; class Container { private: struct Item { Object* pObject; [... other members ...] }; std::list<Item> m_items; };
Ich möchte diese Methoden Container hinzuzufügen:
MagicIterator<IInterface*> Begin(); MagicIterator<IInterface*> End();
Damit Anrufer schreiben:
Container c = [...] for (MagicIterator<IInterface*> i = c.Begin(); i != c.End(); i++) { IInterface* pItf = *i; [...] }
So im Wesentlichen möchte ich eine Klasse schaffen, das Iterieren über eine gewisse Sammlung zu sein scheint (was der Anrufer von Begin () und End () nicht sehen darf) von IInterface Zeigern, aber das ist eigentlich iteriert über eine Sammlung von Zeiger auf andere Objekte (privat an die Container-Klasse), die in IInterface Zeiger umgewandelt werden können.
Ein paar wichtige Punkte:
-
MagicIterator
ist außerhalbContainer
definiert werden. -
Container::Item
muss privat bleiben.
-
MagicIterator
hat iterierenIInterface
Zeiger, trotz der Tatsache, dassContainer
einestd::list<Container::Item>
hält.Container::Item
enthält eineObject*
undObject
kann verwendet werdenIInterface*
zu holen.
-
MagicIterator
hat mit mehreren Klassen wiederverwendbar sein, die Container ähneln, aber vielleicht hat intern andere Liste Implementierungen halten verschiedene Objekte (std::vector<SomeOtherItem>
,mylist<YetAnotherItem>
) und mitIInterface*
in einer anderen Weise jedes Mal erhalten.
-
MagicIterator
sollte nicht Container-spezifischen Code enthalten, obwohl es an Klassen delegieren können, die Sie, sofern eine solche Übertragung nicht schwer zu besonderen Behälter innerhalbMagicIterator
codiert (also irgendwie automatisch durch den Compiler behoben wird, zum Beispiel).
- Die Lösung muss unter Visual C ++ kompilieren, ohne Verwendung anderer Bibliotheken (wie Boost), die eine Lizenzvereinbarung von ihren Autoren erfordern würde.
- Auch Iteration alle Heap-Speicher nicht zuordnen kann (also keine
new()
odermalloc()
in jeder Phase), und keinememcpy()
.
Vielen Dank für Ihre Zeit, auch wenn Sie gerade lesen; Dieser ist wirklich mich worden nervt!
Update: Während ich einige sehr interessante Antworten habe, noch keiner alle oben genannten Voraussetzungen erfüllt sind. Bemerkenswert ist die heikelen Bereiche sind i MagicIterator von Containern Entkopplung) irgendwie (Standardvorlage Argumente schneiden Sie es nicht), und ii) die Vermeidung Zu; aber ich bin wirklich nach einer Lösung, die alle oben genannten Kugeln abdeckt.
Lösung 6
Ich habe nun eine Lösung gefunden, die fitter für meinen ursprünglichen Zweck ist. Ich glaube nicht, es immer noch gerne aber:)
Die Lösung beinhaltet MagicIterator * auf IInterface Templat ist und mit einem void * beide an einen Iterator aufgebaut ist, wobei die Byte-Größe des Iterators, und eine Tabelle von Zeigern auf Funktionen, die auf Standard-Iteration Funktionen ausführen genannte void * wie Inkrement , Abnahme, dereferenzieren usw. MagicIterator geht davon aus, dass es sicher ist, die gegebene Iterator in einen internen Puffer mEMCPY und implementiert ihre eigenen Mitglieder durch einen eigenen Puffer als void * zu den mitgelieferten Funktionen vorbei, als ob es das Original Iterator war.
Container dann statische Iteration Funktionen zu implementieren haben, die zu einem std :: list :: iterator ein geliefertes void * zurückgeworfen. Container :: begin () und Container :: end () konstruieren einfach eine std :: list :: iterator, übergeben Sie einen Zeiger auf sie in eine MagicIterator zusammen mit einer Tabelle der Iterationsfunktionen, und gibt die MagicIterator.
Es ist etwas ekelhaft, und bricht meine ursprüngliche Regel in Bezug auf „no memcpy ()“ und macht Annahmen über die Interna der Iteratoren in Frage. Aber es vermeidet Heapzuordnung, hält Collection Einbauten (einschließlich Item) privat, macht MagicIterator völlig unabhängig von der Sammlung in Frage und dem IInterface *, und in der Theorie erlaubt MagicIterators mit jeder Kollektion zu arbeiten (vorausgesetzt, seine Iteratoren können sicher sein memcopy ()‘ d).
Andere Tipps
Ich glaube, Sie haben zwei getrennte Fragen hier:
Erstellen Sie zunächst einen Iterator, der die IInterface*
von Ihrem list<Container::Item>
zurückkehren wird. Dies ist leicht mit boost::iterator_adaptor
getan:
class cont_iter
: public boost::iterator_adaptor<
cont_iter // Derived
, std::list<Container::Item>::iterator // Base
, IInterface* // Value
, boost::forward_traversal_tag // CategoryOrTraversal
, IInterface* // Reference :)
>
{
public:
cont_iter()
: cont_iter::iterator_adaptor_() {}
explicit cont_iter(const cont_iter::iterator_adaptor_::base_type& p)
: cont_iter::iterator_adaptor_(p) {}
private:
friend class boost::iterator_core_access;
IInterface* dereference() { return this->base()->pObject->GetInterface(); }
};
Sie würden diese Art als Innen in Container
erstellen und zurück in aus seinen begin()
und end()
Methoden.
Zweitens wollen Sie die Runtime-polymorphen MagicIterator
. Das ist genau das, was any_iterator
tut. die MagicIterator<IInterface*>
ist nur any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>
und cont_iter
kann nur zugewiesen werden.
Klingt nicht zu kompliziert. Sie können den Iterator außerhalb definieren. Sie können auch typedefs verwenden. So etwas wie diese passen würde, denke ich. Beachten Sie, dass es wäre viel sauberer, wenn das MagicIterator sei nicht eine kostenlose Vorlage, sondern ein Mitglied von Item, typedefed in Containern vielleicht. So wie es jetzt ist, gibt es eine zyklische Referenz darin, die es necassary einige hässliche Abhilfe Code zu schreiben.
namespace detail {
template<typename T, typename U>
struct constify;
template<typename T, typename U>
struct constify<T*, U*> {
typedef T * type;
};
template<typename T, typename U>
struct constify<T*, U const*> {
typedef T const * type;
};
}
template<typename DstType,
typename Container,
typename InputIterator>
struct MagicIterator;
class Container
{
private:
struct Item
{
Object* pObject;
};
std::list<Item> m_items;
public:
// required by every Container for the iterator
typedef std::list<Item> iterator;
typedef std::list<Item> const_iterator;
// convenience declarations
typedef MagicIterator< IInterface*, Container, iterator >
item_iterator;
typedef MagicIterator< IInterface*, Container, const_iterator >
const_item_iterator;
item_iterator Begin();
item_iterator End();
};
template<typename DstType,
typename Container = Container,
typename InputIterator = typename Container::iterator>
struct MagicIterator :
// pick either const T or T, depending on whether it's a const_iterator.
std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> {
typedef std::iterator<std::input_iterator_tag,
typename detail::constify<
DstType,
typename InputIterator::value_type*>::type> base;
MagicIterator():wrapped() { }
explicit MagicIterator(InputIterator const& it):wrapped(it) { }
MagicIterator(MagicIterator const& that):wrapped(that.wrapped) { }
typename base::value_type operator*() {
return (*wrapped).pObject->GetInterface();
}
MagicIterator& operator++() {
++wrapped;
return *this;
}
MagicIterator operator++(int) {
MagicIterator it(*this);
wrapped++;
return it;
}
bool operator==(MagicIterator const& it) const {
return it.wrapped == wrapped;
}
bool operator!=(MagicIterator const& it) const {
return !(*this == it);
}
InputIterator wrapped;
};
// now that the iterator adepter is defined, we can define Begin and End
inline Container::item_iterator Container::Begin() {
return item_iterator(m_items.begin());
}
inline Container::item_iterator Container::End() {
return item_iterator(m_items.end());
}
Nun starten Sie es:
for(MagicIterator<IInterface*> it = c.Begin(); it != c.End(); ++it) {
// ...
}
Sie können auch einen Iterator mixin durch Schub zur Verfügung gestellt verwenden, die wie die Eingabeversion von boost :: function_output_iterator funktioniert. Er fordert Ihre Iterator operator()
ist, die dann den entsprechenden Wert zurückgibt, zu tun, was wir oben in unseren operator*
grundsätzlich tun. Sie finden es in random/detail/iterator_mixin.hpp
. Das wäre wahrscheinlich in weniger Code führen. Aber es erfordert auch unseren Hals zu zerbrechen, um das Freund-Material sicherzustellen, weil Artikel ist privat und der Iterator nicht innerhalb Artikels definiert. Wie auch immer, viel Glück:)
Eine abstrakte IteratorImplementation
Klasse:
template<typename T>
class IteratorImplementation
{
public:
virtual ~IteratorImplementation() = 0;
virtual T &operator*() = 0;
virtual const T &operator*() const = 0;
virtual Iterator<T> &operator++() = 0;
virtual Iterator<T> &operator--() = 0;
};
Und eine Iterator
Klasse umwickeln es:
template<typename T>
class Iterator
{
public:
Iterator(IteratorImplementation<T> * = 0);
~Iterator();
T &operator*();
const T &operator*() const;
Iterator<T> &operator++();
Iterator<T> &operator--();
private:
IteratorImplementation<T> *i;
}
Iterator::Iterator(IteratorImplementation<T> *impl) :
i(impl)
{
}
Iterator::~Iterator()
{
delete i;
}
T &Iterator::operator*()
{
if(!impl)
{
// Throw exception if you please.
return;
}
return (*impl)();
}
// etc.
(Sie können IteratorImplementation
eine Klasse "innerhalb" von Iterator
machen, um die Dinge sauber zu halten.)
In Ihrer Container
Klasse, gibt eine Instanz von Iterator
mit einer benutzerdefinierten Unterklasse von IteratorImplementation
im ctor
:
class ObjectContainer
{
public:
void insert(Object *o);
// ...
Iterator<Object *> begin();
Iterator<Object *> end();
private:
class CustomIteratorImplementation :
public IteratorImplementation<Object *>
{
public:
// Re-implement stuff here.
}
};
Iterator<Object *> ObjectContainer::begin()
{
CustomIteratorImplementation *impl = new CustomIteratorImplementation(); // Wish we had C++0x's "var" here. ;P
return Iterator<Object *>(impl);
}
Es hängt wirklich von der Container
, da die Rückgabewerte von c.Begin()
und c.End()
sind die Implementierung definiert.
Wenn Sie eine Liste möglicher Container
s bekannt ist zu MagicIterator
, eine Wrapper-Klasse verwendet werden könnte.
template<typename T>
class MagicIterator
{
public:
MagicIterator(std::vector<T>::const_iterator i)
{
vector_const_iterator = i;
}
// Reimplement similarly for more types.
MagicIterator(std::vector<T>::iterator i);
MagicIterator(std::list<T>::const_iterator i);
MagicIterator(std::list<T>::iterator i);
// Reimplement operators here...
private:
std::vector<T>::const_iterator vector_const_iterator;
std::vector<T>::iterator vector_iterator;
std::list<T>::const_iterator list_const_iterator;
std::list<T>::iterator list_iterator;
};
Die einfach Weg wäre, eine Vorlage zu verwenden, die Container
des Typs akzeptiert:
// C++0x
template<typename T>
class Iterator :
public T::iterator
{
using T::iterator::iterator;
};
for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
{
// ...
}
Ich sehe keinen Grund, warum Sie kann das nicht genau implementieren, wie Sie es aus gelegt habe ... bin ich etwas fehlt?
Um zu klären, müssen Sie irgendeine Art von Zugriffsmethoden auf dem Container-Klasse setzen. Sie können privat sein und Sie können MagicIterator als Freund erklären, wenn Sie das Gefühl, das ist der beste Weg, um es zu kapseln, aber ich würde sie direkt aus. Diese Zugriffsmethoden würden eine normale STL Iterator innerhalb Container verwenden und die Umwandlung zu IInterface zuführen. So würden die Iterieren tatsächlich mit den Zugriffsmethoden der Container erfolgen und MagicIterator wäre nur eine Art von Proxy-Objekt, um es einfacher zu machen. Um es einspringenden zu machen, könnten Sie den MagicIterator Pass in irgendeiner Art von ID haben den STL-Iterator innerhalb Container zu suchen, oder man könnte es tatsächlich in dem STL-Iterator als void *
geben hat.
Ein Besucher kann ein einfache (und daher leichter pflegen) Lösung.