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
IntrusiveListNodeand usestatic_cast. In boost this is thebase_hookversion of an intrusive linked list. I would like to stick with themember_hookversion 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. ifTis 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_hookversion 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?