[英]A C++ iterator adapter which wraps and hides an inner iterator and converts the iterated type
玩弄了这个,我怀疑它不可能,但我想我会问专家。 我有以下C ++代码:
class IInterface { virtual void SomeMethod() = 0; }; class Object { IInterface* GetInterface() { ... } }; class Container { private: struct Item { Object* pObject; [... other members ...] }; std::list<Item> m_items; };
我想将这些方法添加到Container:
MagicIterator<IInterface*> Begin(); MagicIterator<IInterface*> End();
为了让呼叫者可以写:
Container c = [...] for (MagicIterator<IInterface*> i = c.Begin(); i != c.End(); i++) { IInterface* pItf = *i; [...] }
所以基本上我想提供一个类似乎迭代某些集合(其中Begin()和End()的调用者不允许看到)的IInterface指针,但它实际上迭代了指向其他指针的指针集合对象(Container类专用),可以转换为IInterface指针。
几个关键点:
MagicIterator
将在Container
外定义。 Container::Item
必须保持私有状态。 MagicIterator
必须迭代IInterface
指针,尽管Container
拥有一个std::list<Container::Item>
。 Container::Item
包含Object*
, Object
可用于获取IInterface*
。 MagicIterator
必须可以重用几个类似于Container的类,但可能在内部有不同的列表实现,每次都以不同的方式获取不同的对象( std::vector<SomeOtherItem>
, mylist<YetAnotherItem>
)和IInterface*
。 MagicIterator
不应该包含特定于容器的代码,尽管它可以委托给那样做的类,只要这样的委托不是硬编码到MagicIterator
特定容器(例如,编译器会以某种方式自动解析)。 new()
或malloc()
),也没有memcpy()
。 感谢您的时间,即使您只是在阅读; 这个人真的在烦我!
更新:虽然我有一些非常有趣的答案,但还没有一个满足上述所有要求。 值得注意的是,棘手的领域是:i)以某种方式将MagicIterator与Container分离(默认模板参数不会削减它),以及ii)避免堆分配; 但我真的想要一个涵盖所有上述子弹的解决方案。
我认为你有两个不同的问题:
首先,创建一个迭代器,它将从list<Container::Item>
返回IInterface*
。 使用boost::iterator_adaptor
很容易做到这一点:
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(); }
};
您可以在Container
创建此类型作为inner,并从其begin()
和end()
方法返回。
其次,您需要运行时多态MagicIterator
。 这正是any_iterator
所做的。 MagicIterator<IInterface*>
只是any_iterator<IInterface*, boost::forward_traversal_tag, IInterface*>
,而cont_iter
只能分配给它。
创建一个抽象的IteratorImplementation
类:
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;
};
还有一个Iterator
它的Iterator
类:
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.
(你可以让IteratorImplementation
类“内部”的Iterator
,以保持整洁。)
在你的Container
类,返回的实例Iterator
与一个自定义子类IteratorImplementation
在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);
}
听起来不太复杂。 您可以在外部定义迭代器。 您也可以使用typedef。 我认为这样的东西是合适的。 请注意,如果MagicIterator 不是免费模板,而是Item的成员,也可以在Container中输入typedefed。 就像现在一样,它中有一个循环引用,这使得编写一些丑陋的变通代码成为必要。
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());
}
现在,开始使用它:
for(MagicIterator<IInterface*> it = c.Begin(); it != c.End(); ++it) {
// ...
}
您还可以使用boost提供的迭代器mixin,它的工作方式类似于boost :: function_output_iterator的输入版本。 它调用你的迭代器的operator()
然后返回适当的值,原则上我们在operator*
中执行上面的operator*
。 你可以在random/detail/iterator_mixin.hpp
找到它。 这可能会导致更少的代码。 但它还需要破坏我们的颈部以确保朋友的东西,因为Item是私有的,并且在Item中没有定义迭代器。 好吧,祝你好运:)
它实际上取决于Container
,因为c.Begin()
和c.End()
的返回值是实现定义的。
如果MagicIterator
知道可能的Container
列表,则可以使用包装类。
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;
};
简单的方法是使用一个接受Container
类型的模板:
// C++0x
template<typename T>
class Iterator :
public T::iterator
{
using T::iterator::iterator;
};
for(Iterator<Container> i = c.begin(); i != c.end(); ++i)
{
// ...
}
我没有理由为什么你不能完全实现这一点,因为你已经解决了......我错过了什么?
为了澄清,您需要在Container类上放置某种访问器方法。 它们可以是私有的,如果您觉得这是封装它的最佳方式,您可以将MagicIterator声明为朋友,但我会直接公开它们。 这些访问器方法将使用Container内的普通STL迭代器并执行到IInterface的转换。 因此,迭代实际上将使用Container的访问器方法完成,而MagicIterator只是一种代理对象,以使其更容易。 为了使它可以重入,你可以让MagicIterator传递某种ID来查找Container里面的STL迭代器,或者你实际上可以让它在STL迭代器中作为void *
传递。
访问者可能是一个更简单(因此更容易维护)的解决方案。
我现在找到了一个适合我原来目的的解决方案。 我仍然不喜欢它:)
解决方案涉及MagicIterator在IInterface *上进行模板化,并且使用void *到迭代器,所述迭代器的字节大小以及指向函数的指针表,这些函数在所述void *上执行标准迭代函数,例如递增,递减, MagicIterator假定将给定迭代器memcpy到内部缓冲区是安全的,并通过将自己的缓冲区作为void *传递给提供的函数来实现自己的成员,就像它是原始迭代器一样。
然后,Container必须实现静态迭代函数,这些函数将提供的void *转换回std :: list :: iterator。 Container :: begin()和Container :: end()只是构造一个std :: list :: iterator,将指向它的指针和一个迭代函数表一起传递给MagicIterator,然后返回MagicIterator。
这有点令人厌恶,并打破了我关于“没有memcpy()”的原始规则,并对所讨论的迭代器的内部进行了假设。 但它避免了堆分配,使Collection的内部(包括Item)保持私有,使MagicIterator完全独立于所讨论的集合和IInterface *,理论上允许MagicIterators使用任何集合(假设它的迭代器可以安全地memcopy()' d)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.