如何从现有的遗留列表创建 boost::intrusive::list?

问题描述

我有这样的遗留结构:

struct LIST_ENTRY {
    LIST_ENTRY *Flink;
    LIST_ENTRY *Blink;
};
LIST_ENTRY legacyListHead;

以及与这样的列表一起使用的遗留代码。如何从中创建 boost::intrusive::list,以便我可以,例如,使用旧的 C 代码和使用 boost::list 添加元素?我可以写节点特征:

struct legacy_node_traits
{
    using node = LIST_ENTRY;
    using node_ptr = node *;
    using const_node_ptr = const node *;

    static node *get_next(const node *n)            {  return n->Flink;  }
    static void set_next(node *n,node *next)       {  n->Flink = next;  }
    static node *get_prevIoUs(const node *n)        {  return n->Blink;  }
    static void set_prevIoUs(node *n,node *prev)   {  n->Blink = prev;  }
};

但是这只允许你创建一个新的,而我只是想从旧的视图中创建一种视图。有可能吗?还是我应该找另一个图书馆/写它?

我想得到的代码示例:

LIST_ENTRY legacyListHead;
...
legacy_push_back(&legacyListHead,&node1);
boost::intrusive::list list{legacyListHead}; // NOT a copy
list.push_back(node2); // node1 -> node2
legacy_push_front(&legacyListHead,&node3); // node3 -> node1 -> node2

解决方法

假设有以下遗留列表:

struct LIST_ENTRY {
    LIST_ENTRY *Flink;
    LIST_ENTRY *Blink;
    int value;
};

LIST_ENTRY nodes[3] = {};
auto a = nodes+0;
auto b = nodes+1;
auto c = nodes+2;
*a = { b,c,13 };
*b = { c,a,42 };
*c = { a,b,-99 };

节点特征允许您使用算法:

using algo = bi::circular_list_algorithms<legacy_node_traits>;

assert(algo::count(a) == 3);
assert(algo::count(b) == 3);
assert(algo::count(c) == 3);

使用值特征,您可以从节点构建一个列表。在您的情况下,可以从节点特征中轻松推断出价值特征:

using List = bi::list<LIST_ENTRY,bi::value_traits<
        bi::trivial_value_traits<legacy_node_traits,bi::normal_link>
    > >;

现在您可以构建一个新列表并使用它:

List l(std::begin(nodes),std::end(nodes)); // CAUTION,see below
for (auto& el : l) {
    std::cout << el.value << "\n";
}

注意:这具有将列表重新排序为数组顺序的副作用,丢失原始链接信息。

如果在 bi::list::splice(它只对已经在另一个 bi::list 中的节点上操作)和 circle_list_algorithms::transfer(它假设相反)之间有一个组合,那就太好了。遗憾的是,这里的明显改进需要访问私有的 get_root_nod()

您可以保证 bi::list 变体(例如可选的大小跟踪!)的唯一方法是编写一个相当愚蠢的循环,例如:

List from_head(LIST_ENTRY* const head) {
    List l;

    auto it = head->Blink;
    while (it != head) {
        auto next = it->Blink;
        l.push_front(*it); // intrusive,so modifies it->Blink!
        it = next;
    }
    l.push_front(*head);
    return l;
}

这很烦人,因为它似乎要求编译器发出代码以再次建立所有相同的链接。希望非常智能的编译器能够真正看穿这一点并消除无操作代码,这并非完全不现实?

完整演示

Live On Coliru

#include <boost/intrusive/trivial_value_traits.hpp>
#include <boost/intrusive/list.hpp>
#include <iostream>

namespace bi = boost::intrusive;

struct LIST_ENTRY {
    LIST_ENTRY *Flink;
    LIST_ENTRY *Blink;
    int value;
};

struct legacy_node_traits
{
    using node = LIST_ENTRY;
    using node_ptr = node *;
    using const_node_ptr = const node *;

    static node *get_next(const node *n)            {  return n->Flink;  }
    static void set_next(node *n,node *next)       {  n->Flink = next;  }
    static node *get_previous(const node *n)        {  return n->Blink;  }
    static void set_previous(node *n,node *prev)   {  n->Blink = prev;  }
};

using List = bi::list<LIST_ENTRY,bi::normal_link>
      > >;

List from_head(LIST_ENTRY* const head) {
    List l;

    auto it = head->Blink;
    while (it != head) {
        auto next = it->Blink;
        l.push_front(*it); // intrusive,so modifies it->Blink!
        it = next;
    }
    l.push_front(*head);
    return l;
}

int main() {
    LIST_ENTRY nodes[3] = {};
    auto a = nodes+0;
    auto b = nodes+1;
    auto c = nodes+2;
    *a = { b,13 };
    *b = { c,42 };
    *c = { a,-99 };

    using algo = bi::circular_list_algorithms<legacy_node_traits>;
    {
        assert(algo::count(a) == 3);
        assert(algo::count(b) == 3);
        assert(algo::count(c) == 3);

        algo::reverse(a);
    }

    List l = from_head(c);
    l.check();
    for (auto& el : l)
        std::cout << el.value << std::endl;
}

印刷品

-99
42
13

我事先将列表颠倒了,以确保我们没有发明自己的链接。此外,l.check() 检查所有节点/列表不变量。