Tidy up List documentation

This commit is contained in:
Vinnie Falco
2013-06-30 09:41:01 -07:00
parent 4d7fe731d8
commit 02f137ace8

View File

@@ -17,289 +17,291 @@
*/ */
//============================================================================== //==============================================================================
#ifndef BEAST_LIST_BEASTHEADER #ifndef BEAST_LIST_H_INCLUDED
#define BEAST_LIST_BEASTHEADER #define BEAST_LIST_H_INCLUDED
struct ListDefaultTag; /** Intrusive Containers
/*============================================================================*/ # Introduction
/**
Intrusive Containers
# Introduction Intrusive containers are special containers that offer better performance
and exception safety guarantees than non-intrusive containers (like the
STL containers). They are useful building blocks for high performance
concurrent systems or other purposes where allocations are restricted
(such as the AudioIODeviceCallback object), because intrusive list
operations do not allocate or free memory.
Intrusive containers are special containers that offer better performance While intrusive containers were and are widely used in C, they became more
and exception safety guarantees than non-intrusive containers (like the and more forgotten in C++ due to the presence of the standard containers
STL containers). They are useful building blocks for high performance which don't support intrusive techniques. VFLib not only reintroduces this
concurrent systems or other purposes where allocations are restricted technique to C++ for lists, it also encapsulates the implementation in a
(such as the AudioIODeviceCallback object), because intrusive list mostly compliant STL interface. Hence anyone familiar with standard
operations do not allocate or free memory. containers can easily use them.
While intrusive containers were and are widely used in C, they became more # Interface
and more forgotten in C++ due to the presence of the standard containers
which don't support intrusive techniques. VFLib not only reintroduces this
technique to C++ for lists, it also encapsulates the implementation in a
mostly compliant STL interface. Hence anyone familiar with standard
containers can easily use them.
# Interface The interface for intrusive elements in this library is unified for all
containers. Unlike STL containers, objects placed into intrusive containers
are not copied. Instead, a pointer to the object is stored. All
responsibility for object lifetime is the responsibility of the caller;
the intrusive container just keeps track of what is in it.
The interface for intrusive elements in this library is unified for all Summary of intrusive container differences:
containers. Unlike STL containers, objects placed into intrusive containers
are not copied. Instead, a pointer to the object is stored. All
responsibility for object lifetime is the responsibility of the caller;
the intrusive container just keeps track of what is in it.
Summary of intrusive container differences: - Holds pointers to existing objects instead of copies.
- Holds pointers to existing objects instead of copies. - Does not allocate or free any objects.
- Does not allocate or free any objects. - Requires a element's class declaration to be modified.
- Requires a element's class declaration to be modified. - Methods never throw exceptions when called with valid arguments.
- Methods never throw exceptions when called with valid arguments. # Usage
# Usage Like STL containers, intrusive containers are all template based, where the
template argument specifies the type of object that the container will hold.
These declarations specify a doubly linked list where each element points
to a user defined class:
Like STL containers, intrusive containers are all template based, where the @code
template argument specifies the type of object that the container will hold.
These declarations specify a doubly linked list where each element points
to a user defined class:
@code struct Object; // Forward declaration
class Object; // Forward declaration List <Object> list; // Doubly-linked list of Object
List <Object> list; // Doubly-linked list of Object @endcode
@endcode Because intrusive containers allocate no memory, allowing objects to be
placed inside requires a modification to their class declaration. Each
intrusive container declares a nested class `Node` which elements must be
derived from, using the Curiously Recurring Template Pattern (CRTP). We
will continue to fully declare the Object type from the previous example
to support emplacement into an intrusive container:
Because intrusive containers allocate no memory, allowing objects to be @code
placed inside requires a modification to their class declaration. Each
intrusive container declares a nested class `Node` which elements must be
derived from, using the Curiously Recurring Template Pattern (CRTP). We
will continue to fully declare the Object type from the previous example
to support emplacement into an intrusive container:
@code struct Object : public List <Object>::Node // Required for List
{
void performAction ();
};
class Object : public List <Object>::Node // Required for List @endcode
{
public:
void performAction ();
};
@endcode Usage of a typedef eliminates redundant specification of the template
arguments but requires a forward declaration. The following code is
equivalent.
Usage of a typedef eliminates redundant specification of the template @code
arguments but requires a forward declaration. The following code is
equivalent.
@code struct Object; // Forward declaration
class Object; // Forward declaration // Specify template parameters just once
typedef List <Object> ListType;
// Specify template parameters just once struct Object : public ListType::Node
typedef List <Object> ListType; {
void performAction ();
};
class Object : public ListType::Node ListType::Node list;
{
void performAction ();
};
ListType::Node list; @endcode
@endcode With these declarations we may proceed to create our objects, add them to
the list, and perform operations:
With these declarations we may proceed to create our objects, add them to @code
the list, and perform operations:
@code // Create a few objects and put them in the list
for (i = 0; i < 5; ++i)
list.push_back (*new Object);
// Create a few objects and put them in the list // Call a method on each list
for (i = 0; i < 5; ++i) for (ListType::iterator iter = list.begin(); iter != list.end (); ++iter)
list.push_back (*new Object); iter->performAction ();
// Call a method on each list @endcode
for (ListType::iterator iter = list.begin(); iter != list.end (); ++iter)
iter->performAction ();
@endcode Unlike regular STL containers, an object derived from an intrusive container
node cannot exist in more than one instance of that list at a time. This is
because the bookkeeping information for maintaining the list is kept in
the object rather than the list.
Unlike regular STL containers, an object derived from an intrusive container To support objects existing in multiple containers, templates variations
node cannot exist in more than one instance of that list at a time. This is are instantiated by distinguishing them with an empty structure, called a
because the bookkeeping information for maintaining the list is kept in tag. The object is derived from multiple instances of Node, where each
the object rather than the list. instance specifies a unique tag. The tag is passed as the second template
argument. When the second argument is unspecified, the default tag is used.
To support objects existing in multiple containers, templates variations This declaration example shows the usage of tags to allow an object to exist
are instantiated by distinguishing them with an empty structure, called a simultaneously in two separate lists:
tag. The object is derived from multiple instances of Node, where each
instance specifies a unique tag. The tag is passed as the second template
argument. When the second argument is unspecified, the default tag is used.
This declaration example shows the usage of tags to allow an object to exist @code
simultaneously in two separate lists:
@code struct GlobalListTag { }; // list of all objects
struct ActiveListTag { }; // subset of all objects that are active
struct GlobalListTag { }; // list of all objects class Object : public List <Object, GlobalListTag>
struct ActiveListTag { }; // subset of all objects that are active , public List <Object, ActiveListTag>
{
class Object : public List <Object, GlobalListTag> public:
, public List <Object, ActiveListTag>
{
public:
Object () : m_isActive (false) Object () : m_isActive (false)
{ {
// Add ourselves to the global list // Add ourselves to the global list
s_globalList.push_front (*this); s_globalList.push_front (*this);
} }
~Object () ~Object ()
{ {
deactivate (); deactivate ();
} }
void becomeActive () void becomeActive ()
{ {
// Add ourselves to the active list // Add ourselves to the active list
if (!m_isActive) if (!m_isActive)
{ {
s_activeList.push_front (*this); s_activeList.push_front (*this);
m_isActive = true; m_isActive = true;
} }
} }
void deactivate () void deactivate ()
{ {
if (m_isActive) if (m_isActive)
{ {
// Doesn't delete the object // Doesn't delete the object
s_activeList.erase (s_activeList.iterator_to (this)); s_activeList.erase (s_activeList.iterator_to (this));
m_isActive = false; m_isActive = false;
} }
} }
private: private:
bool m_isActive; bool m_isActive;
static List <Object, GlobalListTag> s_globalList; static List <Object, GlobalListTag> s_globalList;
static List <Object, ActiveListTag> s_activeList; static List <Object, ActiveListTag> s_activeList;
} }
@endcode @endcode
@defgroup intrusive intrusive @defgroup intrusive intrusive
@ingroup beast_core @ingroup beast_core
*/ */
/*============================================================================*/ //------------------------------------------------------------------------------
/** Default tag for List.
@ingroup beast_core intrusive
*/
struct ListDefaultTag;
/** /**
Intrusive doubly linked list. Intrusive doubly linked list.
This intrusive List is a container similar in operation to std::list in the This intrusive List is a container similar in operation to std::list in the
Standard Template Library (STL). Like all @ref intrusive containers, List Standard Template Library (STL). Like all @ref intrusive containers, List
requires you to first derive your class from List<>::Node: requires you to first derive your class from List<>::Node:
@code @code
struct Object : List <Object>::Node struct Object : List <Object>::Node
{
Object (int value) : m_value (value)
{ {
} explicit Object (int value) : m_value (value)
{
int m_value; }
};
int m_value;
@endcode };
Now we define the list, and add a couple of items. @endcode
@code Now we define the list, and add a couple of items.
List <Object> list; @code
list.push_back (* (new Object (1))); List <Object> list;
list.push_back (* (new Object (2)));
list.push_back (* (new Object (1)));
@endcode list.push_back (* (new Object (2)));
For compatibility with the standard containers, push_back() expects a @endcode
reference to the object. Unlike the standard container, however, push_back()
places the actual object in the list and not a copy-constructed duplicate. For compatibility with the standard containers, push_back() expects a
reference to the object. Unlike the standard container, however, push_back()
Iterating over the list follows the same idiom as the STL: places the actual object in the list and not a copy-constructed duplicate.
@code Iterating over the list follows the same idiom as the STL:
for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter) @code
std::cout << iter->m_value;
for (List <Object>::iterator iter = list.begin(); iter != list.end; ++iter)
@endcode std::cout << iter->m_value;
You can even use BOOST_FOREACH, or range based for loops: @endcode
@code You can even use BOOST_FOREACH, or range based for loops:
BOOST_FOREACH (Object& object, list) // boost only @code
std::cout << object.m_value;
BOOST_FOREACH (Object& object, list) // boost only
for (Object& object : list) // C++11 only std::cout << object.m_value;
std::cout << object.m_value;
for (Object& object : list) // C++11 only
@endcode std::cout << object.m_value;
Because List is mostly STL compliant, it can be passed into STL algorithms: @endcode
e.g. `std::for_each()` or `std::find_first_of()`.
Because List is mostly STL compliant, it can be passed into STL algorithms:
In general, objects placed into a List should be dynamically allocated e.g. `std::for_each()` or `std::find_first_of()`.
although this cannot be enforced at compile time. Since the caller provides
the storage for the object, the caller is also responsible for deleting the In general, objects placed into a List should be dynamically allocated
object. An object still exists after being removed from a List, until the although this cannot be enforced at compile time. Since the caller provides
caller deletes it. This means an element can be moved from one List to the storage for the object, the caller is also responsible for deleting the
another with practically no overhead. object. An object still exists after being removed from a List, until the
caller deletes it. This means an element can be moved from one List to
Unlike the standard containers, an object may only exist in one list at a another with practically no overhead.
time, unless special preparations are made. The Tag template parameter is
used to distinguish between different list types for the same object, Unlike the standard containers, an object may only exist in one list at a
allowing the object to exist in more than one list simultaneously. time, unless special preparations are made. The Tag template parameter is
used to distinguish between different list types for the same object,
For example, consider an actor system where a global list of actors is allowing the object to exist in more than one list simultaneously.
maintained, so that they can each be periodically receive processing
time. We wish to also maintain a list of the subset of actors that require For example, consider an actor system where a global list of actors is
a domain-dependent update. To achieve this, we declare two tags, the maintained, so that they can each be periodically receive processing
associated list types, and the list element thusly: time. We wish to also maintain a list of the subset of actors that require
a domain-dependent update. To achieve this, we declare two tags, the
@code associated list types, and the list element thusly:
struct Actor; // Forward declaration required @code
struct ProcessTag { }; struct Actor; // Forward declaration required
struct UpdateTag { };
struct ProcessTag { };
typedef List <Actor, ProcessTag> ProcessList; struct UpdateTag { };
typedef List <Actor, UpdateTag> UpdateList;
typedef List <Actor, ProcessTag> ProcessList;
// Derive from both node types so we can be in each list at once. typedef List <Actor, UpdateTag> UpdateList;
//
struct Actor : ProcessList::Node, UpdateList::Node // Derive from both node types so we can be in each list at once.
{ //
bool process (); // returns true if we need an update struct Actor : ProcessList::Node, UpdateList::Node
void update (); {
}; bool process (); // returns true if we need an update
void update ();
@endcode };
@tparam Element The base type of element which the list will store @endcode
pointers to.
@tparam Element The base type of element which the list will store
@tparam Tag An optional unique type name used to distinguish lists and nodes, pointers to.
when the object can exist in multiple lists simultaneously.
@tparam Tag An optional unique type name used to distinguish lists and nodes,
@ingroup beast_core intrusive when the object can exist in multiple lists simultaneously.
@ingroup beast_core intrusive
*/ */
template <class Element, class Tag = ListDefaultTag> template <class Element, class Tag = ListDefaultTag>
class List : Uncopyable class List : Uncopyable
@@ -786,11 +788,4 @@ private:
Node m_tail; Node m_tail;
}; };
/**
Default tag for List.
@ingroup beast_core intrusive
*/
struct ListDefaultTag { };
#endif #endif