2

タイプ セーフな container_of メンバー関数を持つテンプレート クラスとして内部リストを定義しようとしています。そのためには、テンプレートにコンテナーのタイプと、コンテナー内でリストを見つけることができるオフセット (メンバー ポインター) を含める必要があります。(C の例については、以下を参照してください)。

次のようになります。

template <class T, List * T::*MEMBER> class List { ... }

しかし <> では、タイプ List はまだ定義されていないため、使用できません。私の次の試みは:

template <class T, class L, L * T::*MEMBER> class List { ... };

class Container {
    List<Container, List<???>, Container::list> list;
};

しかし、「???」には何を入れるのですか?それは、??? を含む <> 全体でなければなりません。したがって、無限の再帰が得られます。

次に、型の安全性について少しごまかそうとしました。

template <class T, void * T::*M>
class List {
public:
    T * container_of() {
        return (T *)(intptr_t(this) - intptr_t(&((T *)NULL)->M)); \
    }
};

class Container {
public:
    List<Container, Container::item1> item1;
};

しかし、それは私に与えます:

error: incomplete type 'Container' used in nested name specifier
       List<Container, Container::item1> item1;
                       ^

Cプリプロセッサマクロを使用すると、次のようになります。

#include <unistd.h> // for NULL
#include <stdint.h> // for intptr_t
#include <iostream>

#define LIST(TYPE, MEMBER) \
class List_ ## MEMBER ## _t { \
public: \
    TYPE * container_of() { \
    return (TYPE *)(intptr_t(this) - intptr_t(&((TYPE *)NULL)->MEMBER)); \
    } \
} MEMBER

class Container {
public:
    LIST(Container, item1);
    LIST(Container, item2);
};

int main() {
    Container c;
    std::cout << "Container at " << &c << std::endl;
    std::cout << "Container of item1 = " << c.item1.container_of() << std::endl;
    std::cout << "Container of item2 = " << c.item2.container_of() << std::endl;
}

では、これはテンプレートで表現できますか?

4

1 に答える 1

0

解決策を見つけました。100%完璧ではありませんが、ほぼ完璧です。

アイデアは、3つのクラスを持つことです:

class Item;
template <class T, Item T::*M> class Iterator;
template <class T, Item T::*M> class Head;

Item クラスには、メモリ内の実際のリストを形成する次/前のリンクが含まれています。これには、コンテナーの種類とコンテナー内のリストの位置は含まれず、(それ自体では) 安全ではありません。しかし、Item にはリストを変更するメソッドがありません。すべての変更は Iterator を通じて行われます。Head を使用して Iterator を取得し、それを使用して next/prev ポインターを初期化することもできます。

Iterator クラスはコンテナー T から構築でき、演算子 ++、--、==、および != を持ち、コンテナーを現在の位置に挿入したり、コンテナーを別のイテレーターの背後にある独自のリストに移動したりできます。Iterator には、現在のコンテナーを返す演算子 * と、リストの最後に到達したかどうかを示す演算子 bool もあります。

Head クラスには、それぞれ prev==NULL と next==NULL を持つ特別な head と tail Item が含まれています。それらはコンテナ T のインスタンス内になく、リストの開始と終了をマークするため、特別です。終了マーカーを保持する以外に、Head は、先頭、末尾、最初と最後の要素を指す Iterator を作成するメソッドを提供します。これにより、リストを繰り返したり、最初または最後に挿入したりできます。

Iterator に似ていますが、const アクセス用の 4 番目のクラス ConstIterator があります。

注: これは最小限のテストのみです。残りのエラーは、読者が修正するために残されています。


class Item;
template <class T, Item T::*M> class Iterator;
template <class T, Item T::*M> class ConstIterator;
template <class T, Item T::*M> class Head;

template<class T, Item T::*M>
T * container_of(Item *item) {
    return (T *)(intptr_t(item) - intptr_t(&(((T *)NULL)->*M)));
}

template<class T, Item T::*M>
const T * container_of(const Item *item) {
    return (const T *)(intptr_t(item) - intptr_t(&(((const T *)NULL)->*M)));
}

class Item {
public:
    template <class T, Item T::*M> Item(Head<T, M> *head, T *container) {
        assert((container_of<T, M>(this)) == container);
        head->tail().insert_before(container);
    }
    ~Item() {
        if (next_) next_->prev_ = prev_;
        if (prev_) prev_->next_ = next_;
        next_ = NULL;
        prev_ = NULL;
    }
private:
    template <class T, Item T::*M> friend class Iterator;
    template <class T, Item T::*M> friend class ConstIterator;
    template <class T, Item T::*M> friend class Head;
    Item(Item *next__, Item *prev__) : next_(next__), prev_(prev__) { }
    Item(const Item &) = delete;
    Item & operator =(const Item &) = delete;
    Item *next_;
    Item *prev_;
};

template <class T, Item T::*M>
class Iterator {
public:
    Iterator() : item_(NULL) { }
    Iterator(T *container) : item_(&(container->*M)) { }
    ~Iterator() { }
    operator bool() const {
        assert(item_);
        // not head and not tail
        return ((item_->next_ != NULL) && (item_->prev_ != NULL));
    }
    T & operator *() {
        assert(item_);
        if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
            // head or tail has no container
            assert(false);
        }
        return *container_of<T, M>(item_);
    }
    T & operator ->() {
        assert(item_);
        if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
            // head or tail has no container
            assert(false);
        }
        return *container_of<T, M>(item_);
    }
    Iterator & operator ++() {
        assert(item_);
        assert(item_->next_);
        item_ = item_->next_;
        return *this;
    }
    Iterator & operator --() {
        assert(item_);
        assert(item_->prev_);
        item_ = item_->prev_;
        return *this;
    }
    bool operator ==(const Iterator &other) {
        assert(item_);
        return (item_ == other.item_);
    }
    bool operator !=(const Iterator &other) {
        assert(item_);
        return (item_ != other.item_);
    }
    void move_before(Iterator &from) {
        assert(item_);
        assert(from);
        assert(item_->prev_);

        Item *before = item_->prev_;
        Item *after = item_;
        Item *item = from.item_;

        // remove from old list
        item->next_->prev_ = item->prev_;
        item->prev_->next_ = item->next_;

        // insert into this list
        item->next_ = after;
        item->prev_ = before;
        before->next_ = item;
        after->prev_ = item;
    }
    void insert_before(T *container) {
        assert(item_);
        assert(item_->prev_);

        Item *before = item_->prev_;
        Item *after = item_;
        Item *item = &(container->*M);

        // insert into this list
        item->next_ = after;
        item->prev_ = before;
        before->next_ = item;
        after->prev_ = item;
    }
private:
    Item *item_;
};

template <class T, Item T::*M>
class ConstIterator {
public:
    ConstIterator() : item_(NULL) { }
    ConstIterator(const T *container) : item_(&(container->*M)) { }
    ~ConstIterator() { }
    operator bool() const {
        assert(item_);
        // not head and not tail
        return ((item_->next_ != NULL) && (item_->prev_ != NULL));
    }
    const T & operator *() const {
        assert(item_);
        if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
            // head or tail has no container
            assert(false);
        }
        return *container_of<T, M>(item_);
    }
    const T & operator ->() const {
        assert(item_);
        if ((item_->next_ == NULL) || (item_->prev_ == NULL)) {
            // head or tail has no container
            assert(false);
        }
        return *container_of<T, M>(item_);
    }
    ConstIterator & operator ++() {
        assert(item_);
        assert(item_->next_);
        item_ = item_->next_;
        return *this;
    }
    ConstIterator & operator --() {
        assert(item_);
        assert(item_->prev_);
        item_ = item_->prev_;
        return *this;
    }
    bool operator ==(const ConstIterator &other) const {
        assert(item_);
        return (item_ == other.item_);
    }
    bool operator !=(const ConstIterator &other) {
        assert(item_);
        return (item_ != other.item_);
    }
private:
    const Item *item_;
};

template <class T, Item T::*M>
class Head {
public:
    Head() : head_(&tail_, NULL), tail_(NULL, &head_) { }
    ~Head() { }
    Iterator<T, M> head() {
        return Iterator<T, M>(container_of<T, M>(&head_));
    }
    ConstIterator<T, M> head() const {
        return ConstIterator<T, M>(container_of<T, M>(&head_));
    }
    Iterator<T, M> tail() {
        return Iterator<T, M>(container_of<T, M>(&tail_));
    }
    ConstIterator<T, M> tail() const {
        return ConstIterator<T, M>(container_of<T, M>(&tail_));
    }
    Iterator<T, M> first() {
        return Iterator<T, M>(container_of<T, M>(head_.next_));
    }
    ConstIterator<T, M> first() const {
        return ConstIterator<T, M>(container_of<T, M>(head_.next_));
    }
    Iterator<T, M> last() {
        return Iterator<T, M>(container_of<T, M>(tail_.prev_));
    }
    ConstIterator<T, M> last() const {
        return ConstIterator<T, M>(container_of<T, M>(tail_.prev_));
    }
    bool is_empty() const {
        return (first() == tail());
    }
private:
    Head(const Head &) = delete;
    Head & operator =(const Head &) = delete;
    Item head_;
    Item tail_;
};
于 2014-11-19T23:47:19.847 に答える