I'm trying to define an internal list as template class that has a type safe container_of member function. For that the template must include the type of the container and the offset where in the container the list can be found (a member pointer). (See below for an example in C).
It should be something like this:
template <class T, List * T::*MEMBER> class List { ... }
But in the <> the type List is not yet defined so it can't be used. My next try was:
template <class T, class L, L * T::*MEMBER> class List { ... };
class Container {
List<Container, List<???>, Container::list> list;
};
But what to put for the "???"? That would have to be the whole <>, including the ???. So you get an endless recursion.
Next I tried to cheat a bit on the type safety:
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;
};
But that gives me:
error: incomplete type 'Container' used in nested name specifier
List<Container, Container::item1> item1;
^
Using C preprocessor makros what I want looks like this:
#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;
}
So can this be expressed with templates at all?
I found a solution. It's not 100% perfect but close.
The idea is to have 3 classes:
The Item class contains the next/prev links that form the actual list in memory. This doesn't include the container type and position of the list inside the container and (on its own) is unsafe. But an Item has no method to modify the list. All modifications are done through the Iterator. Even construction is done using a Head to get an Iterator and that to initialize the next/prev pointers.
The Iterator class can be constructed from a container T and has operator ++, --, == and !=, can insert a container into the current position or move the container behind another iterator to its own list. The Iterator also has operator * which returns the current container and operator bool to say if the end of the list has been reached.
The Head class contains a special head and tail Item with prev==NULL and next==NULL respectively. They are special since they aren't inside an instance of container T and mark the begining and end of the list. Other than holding the end markers the Head provides methods to create Iterators pointing at the head, tail, first and last element. This allows iterating the list or inserting at the start or end.
There is a 4th class ConstIterator that is like Iterator but for const access.
Note: This is only minimally tested. The remaining errors are left for the reader to fix.