CDSA – A library of generic data structures and algorithms in ANSI C
github.com
github.com
For those, like me, who did not know what makes a data structure "intrusive", it means that the things needed for the data structure are stored together with the data.
For example, suppose our application needs to keep track of people. As far as we are concerned, a person looks like this:
struct person
{
int age;
const char * name;
};
If we want to make a linked list of people, and do so by adding a link field to our person struct, like this: struct person
{
person * next_person;
int age;
const char * name;
};
and then make our list by linking our person structures through the next_person field, that would be an intrusive linked list.A non-intrusive linked list would be to do something like this:
struct list_element
{
list_element * next;
void * data;
};
and then make our linked list by making a linked list of list_element structures, with their data pointers pointing to our person structures. If we were using C++, another way to do a non-intrusive linked list of people would be to use std::list<person>.Another great part about intrusive data structures is that you can place an object on multiple structures without having to allocate any separate data for them. So to give an example, `std::list<person>` will actually create an 'intrusive' list - it will store the node information along with `person`. However, that `person` is completely tied to the list. If you want to take a `person` off of that list and place it into another one, you can't, you would have to either copy it into a node on the other list, or make the all the lists `std::list<person *>` and then store a pointer to the same person in both lists. With intrusive structures, you can instead use the same set of node information for every list, so you can detach a node from the first list, and then attach it to the second one without having to do any allocations.
As a note, the Linux Kernel makes fairly extensive use of intrusive data-structures, which is an area they particularly shine in since you really want to avoid all those small allocations if you can.
http://www.boost.org/doc/libs/1_66_0/doc/html/intrusive.html
There's also intrusive_ptr, an intrusive reference-counted smart-pointer class template.
http://www.boost.org/doc/libs/1_66_0/libs/smart_ptr/doc/html...
[1] - ziglang.orgThat way, users of the library would have no direct access to the fields of the struct (e.g. hashtable->num_buckets;), they'd have to use a function declared at the correspondent header file. (e.g. get_num_buckets(hashtable);).
Direct access to member fields could break the data structure because data structures always needs to follow certain rules in insertion and deletion.
You can get away with forward declarations of things if you only ever need a pointer to the struct, because the compiler knows how big a pointer is and only needs to know the size of the structure when you dereference it.
You also need to know the size when you allocate it - whether you're using malloc, or allocating statically. As you want to leave control of allocation up to the caller, then the caller needs to know the size.
Theoretically I suppose you could allocate through a macro that used an explicit size number in the header, but going to those lengths to take the struct out of the header seems a bit extreme.
The sad part is that the more fancy header functions and tricks that gets added to a library like this, the closer you are to reinventing C++ :)