Предоставление итераторов c ++ для устаревшего интерфейса связанных списков C - PullRequest
0 голосов
/ 08 апреля 2011

У меня есть приложение Visual Studo 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: %s\r\n", 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; 
};

Ответы [ 4 ]

3 голосов
/ 08 апреля 2011

Не усложняйте:

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; }
}
1 голос
/ 08 апреля 2011

Я считаю, что ваш value_type неверен ... если вы наследуете итератор STL в качестве интерфейса, тип значения должен быть того типа, который вы содержите. В вашем случае вы говорите, что у вас есть элементы MyInfo, но вы пытаетесь вернуть LINKED_LIST_INFO*

Либо верните MyInfo, либо объявите, что ваш контейнер итератора имеет value_type из LINKED_LIST_INFO.

Возможно, вам нужен первый вариант, так как вам может понадобиться вспомогательный класс для методов доступа, чтобы иметь возможность правильно манипулировать членами структуры.

Edit:

Естественно, вы должны понимать, что просто наличие std::vector<LINKED_LIST_INFO> или std::vector<MyInfo> дает вам всю необходимую функциональность без каких-либо проблем с внедрением и обслуживанием.

Редактировать # 2:

На самом деле у вас не может быть std::vector<MyInfo>, потому что он не является конструируемым по умолчанию, поэтому, как только вы исправите свою текущую ошибку, вы получите другую, основанную на typedef std::vector< MyInfo > MyBuffer;, которая не будет разрешена в конструкции шаблона, пока вы не предоставите конструктор по умолчанию для MyInfo

Редактировать # 3:

Ваш value_type operator*() { return *elem_; } сформирован неправильно. Вы не должны возвращать копию внутреннего объекта после этой двоичной операции. Вы должны вернуть ссылку. В вашем случае вы воспринимаете это как разыменование, а не как операцию умножения, что хорошо, но возвращаемое при копировании по значению значение по-прежнему неверно.

0 голосов
/ 09 апреля 2011

Это ужасно (не стоит этого делать):

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

0 голосов
/ 08 апреля 2011

Похоже, что просто возвращение адреса elem_ должно сделать это.

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

Вы уверены, что было бы не так сложно изменить свой прежний список на std::list или std::vector в зависимости от ситуации?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...