为遗留的c链表接口提供c++迭代器

Providing c++ iterators for a legacy C linked-list interface

本文关键字:c++ 迭代器 接口 链表      更新时间:2023-10-16

我有一个Visual studio 2008 c++应用程序,我试图将迭代器支持添加到遗留的C- api的链表结构中。C语言的接口是这样的:

typedef struct _LINKED_LIST_INFO {
    struct _LINKED_LIST_INFO* Next;
    const char* name;
    // more elements. some are fixed-size; others are pointers to other structures.
} LINKED_LIST_INFO;
DWORD GetLinkedList( LINKED_LIST_INFO* pInfo, PULONG pOutBufLen );

我希望能够这样使用它:

int _tmain( int /*argc*/, _TCHAR* /*argv*/[] )
{
    MyLinkedList elements;
    for( MyLinkedList::const_iterator it = elements.begin();
         it != elements.end();
         ++it )
    {
        printf( "Name: %srn", it->Name().c_str() );
    }
    return 0;
}

我创建了这3个类。但是,我的MyInfoIterator班有operator->()的问题。我不能返回MyInfo的临时指针,所以我得到错误:error C2440: 'return' : cannot convert from 'MyInfo' to 'const MyInfo *'

这个问题的好解决方案是什么?
/// wrap the legacy C structure and provide C++ accessors
class MyInfo
{
public:
    MyInfo( const LINKED_LIST_INFO& info ) : elem_( info ) { };
    std::string Name() const { return elem_.name; };
private:
    /// one element of the linked list
    const LINKED_LIST_INFO& elem_;
}; // class MyInfo
namespace detail {
/// buffer to hold the legacy C linked-list
typedef std::vector< BYTE > MyBuffer;
/// iterator support for the legacy C linked-list
class MyInfoIterator 
    : public std::iterator< std::input_iterator_tag, MyInfo > 
{
public:
    explicit MyInfoIterator( MyBuffer& list ) : data_( list )
    {
        elem_ = reinterpret_cast< LINKED_LIST_INFO* >( &data_.front() );
    };
    MyInfoIterator() : elem_( NULL ) { };
    MyInfoIterator& operator++() 
    {
        elem_ = elem_->Next;
        return *this;
    };
    value_type operator*() { return *elem_; };
    //  error C2440: 'return' : cannot convert from 'MyInfo' to 'const MyInfo *'
    const value_type* operator->() { return elem_; };
    friend bool operator==( const MyInfoIterator& i, 
                            const MyInfoIterator& j ) 
    { 
        return i.elem_ == j.elem_;
    };
private:
    /// linked-list of elements
    MyBuffer data_;
    /// current position within the element list
    LINKED_LIST_INFO* elem_;
}; // class MyInfoIterator
bool operator!=( const MyInfoIterator& i, const MyInfoIterator& j ) 
{ 
    return !operator==( i, j );
}
}; // namespace detail
/// provide iterator access for the legacy C linked-list API
class MyLinkedList
{
public:
    typedef detail::MyInfoIterator const_iterator;
    const_iterator begin() const 
    { 
        ULONG size = sizeof( LINKED_LIST_INFO );
        detail::MyBuffer buffer( size );
        DWORD ec = ::GetLinkedList( 
            reinterpret_cast< LINKED_LIST_INFO* >( &buffer.front() ), &size );
        if( ERROR_BUFFER_OVERFLOW == ec )
        {
            buffer.resize( size );
            ec = ::GetLinkedList( 
                reinterpret_cast< LINKED_LIST_INFO* >( &buffer.front() ), &size );
        }
        if( ERROR_SUCCESS != ec )
            Win32Exception::Throw( ec );
        return const_iterator( buffer ); 
    };
    const_iterator end() const { return const_iterator(); };
}; // class MyInfo

谢谢,PaulH

编辑:

我不能改变旧的API或它的相关结构。

Edit2:

我认为我有一个工作的解决方案,保留我隐藏底层链表实现的意图,并通过返回static MyInfo的地址来维护每个类的职责分离。

class MyInfo
{
   // ...
protected:
    MyInfo() : info_( NULL ) { };
    void Set( const LINKED_LIST_INFO* info ) { info_ = info; };
private:
    friend MyInfoIterator;
    const LINKED_LIST_INFO* info_;
};
const value_type& MyInfoIterator::operator*() const 
{ 
    static MyInfo info;
    info.Set( elem_ );
    return info;
};
const value_type* MyInfoIterator::operator->() const
{ 
    static MyInfo info;
    info.Set( elem_ );
    return &info; 
};

保持简洁:

class MyInfoIterator 
    : public std::iterator< std::input_iterator_tag, _LINKED_LIST_INFO >
{
   _LINKED_LIST_INFO* p;
public;
    MyInfoIterator(_LINKED_LIST_INFO* pointer) : p(pointer) {}
    [Same as you did]
    value_type& operator*() { return *p; }
}

我相信你的value_type是错误的…如果继承STL迭代器作为接口,则值类型需要是包含的类型。在您的例子中,您说您包含MyInfo的元素,但您试图返回LINKED_LIST_INFO*

返回MyInfo或者声明你的迭代器容器的value_typeLINKED_LIST_INFO

可能需要第一个选项,因为您可能需要访问方法的帮助类,以便能够正确地操作结构体的成员。

编辑:

很自然地,你应该意识到,只要有一个std::vector<LINKED_LIST_INFO>std::vector<MyInfo>就可以提供你所需要的所有功能,而没有实现和维护问题。

编辑# 2:

实际上你不能有std::vector<MyInfo>因为它不是默认的可构造函数所以一旦你解决了当前的错误你会得到另一个基于typedef std::vector< MyInfo > MyBuffer;的错误除非你为MyInfo提供默认构造函数否则在模板构建中将无法解决

编辑# 3:

您的value_type operator*() { return *elem_; }格式相当不正确。在此二进制操作之后,不应该返回内部对象的副本。你应该返回一个引用。在您的情况下,您将此视为一个推导而不是一个乘法操作,这是可以的,但按值复制返回仍然是错误的。

这太可怕了(你不应该这样做):

class MyInfo {
   char* name() const {
      return ((_LINKED_LIST_INFO*)this)->name;
   }
};
class MyInfoIterator 
: public std::iterator< std::input_iterator_tag, MyInfo>
{
   _LINKED_LIST_INFO* p;
public:
    MyInfoIterator(LINKED_LIST_INFO* pointer) : p(pointer) {}
    [Same as you did]
    reference operator*() const { return *(MyInfo*)p; }
};

干净的方法可能是:

class MyInfo {
public:
  [...]
  private:
    const LINKED_LIST_INFO* elem_;
}
class MyInfoIterator 
: public std::iterator< std::input_iterator_tag, MyInfo>
{
   mutable MyInfo p;
public:
    MyInfoIterator(LINKED_LIST_INFO* pointer) : p(pointer) {}
    [...]
    reference& operator*() const { return p; }
    pointer_type operator->() const { return &p; }
};

这是一些Boost迭代器的实现方式(我指的是干净的方式,不是第一个),例如参见[1]。

[1] http://www.boost.org/doc/libs/1_46_1/libs/iterator/doc/counting_iterator.html

看起来只要返回elem_的地址就可以了。

const value_type* operator->() { return &elem_; };

您确定将您的遗留列表更改为适当的std::liststd::vector不会减少工作量吗?