For the third time in a few years I find myself needing an intrusive linked list for a project that does not allow boost (ask management...).
For the third time I find that the intrusive linked list implementation I have works perfectly, but that I really don't like that it makes use of undefined behavior - namely when converting the pointer to a list node into a pointer to the object containing that list node.
That awful code currently looks like this:
struct IntrusiveListNode {
IntrusiveListNode * next_;
IntrusiveListNode * prev_;
};
template <typename T, IntrusiveListNode T::*member>
class IntrusiveList {
// snip ...
private:
T & nodeToItem_(IntrusiveListNode & node) {
return *(T*)(((char*)&node)-((size_t)&(((T*)nullptr)->*member)));
}
IntrusiveListNode root_;
};
I don't really care how ugly nodeToItem_
gets, but I would like to keep the public interface and syntax of IntrusiveList
the same. Specifically, I would like to specify the type of a list type using IntrusiveList<Test, &Test::node_>
rather than IntrusiveList<Test, offsetof(Test, node_)>
.
It's almost 2016 - is there any way to do this without invoking undefined behavior?
Edit: There have been a few suggested solutions (involving different structures of the list) in the comments which I want to summarize here:
Live with undefined behavior, since the language has seemingly arbitrary limitations that prevent using member pointers in reverse.
Store an additional pointer to the containing class within
IntrusiveListNode
. This is currently probably the cleanest solution (no change in interface necessary), but does require a third pointer in every list node (small optimizations may be possible).Derive from
IntrusiveListNode
and usestatic_cast
. In boost this is thebase_hook
version of an intrusive linked list. I would like to stick with themember_hook
version to avoid introducing multiple inheritance.Store pointers to the next and previous containing class instead of to the next and previous list node within
IntrusiveListNode
. This makes creating the root node within the intrusive list difficult. Either the list must include a full instantiation ofT
(which is not possible e.g. ifT
is abstract), or the end of the list would need to be a null pointer (which would break--list.end()
, allowing forward iteration only).Boost intrusive lists have a
member_hook
version that works somehow, but the implementation has not been understood (and it possibly also relies on undefined behavior).
The question remains: is it possible to make an intrusive member-based list with bidirectional iteration support, no undefined behavior, and no "unnecessary" memory overhead?