Classes | Typedefs | Functions | Variables
Sawyer::Attribute Namespace Reference


Attributes attached to other objects.

This namespace defines a mechanism for extending objects at run-time through the use of name/value pairs called "attributes". Attributes are an extension mechanism orthogonal to class derivation and compile-time containment. Among other things, they are a convenient mechanism for users to cache data that needs to be associated with some other object. There are two steps to using attributes: registering an attribute name to obtain an ID number, and accessing attribute values stored in objects.

Registering attribute names

In order to be able to store many attribute name/value pairs per object, the attribute names must be declared by registering them with the library. The names are entered into a global symbol table and must be unique. Each name will be given an ID number to use when values are stored and retrieved.

#include <Sawyer/Attribute.h>
const Sawyer::Attribute::Id DUCKS = Sawyer::Attribute::declare("number of ducks");
const Sawyer::Attribute::Id WINDOW = Sawyer::Attribute::declare("GUI window information");
const Sawyer::Attribute::Id GRAPHVIZ = Sawyer::Attribute::declare("GraphViz output file");

Once an ID number is created it will never be removed from the attribute symbol table; attribute ID numbers are stable for the life of the program. If the same name is registered more than once then an Attribute::AlreadyExists exception is thrown. The Attribute::id and Attribute::name functions can be used to obtain the ID number for a name, or the name for an ID number, respectively.

Storing and retrieving attributes

Each object can store zero or one value per declared attribute, and those values can be any type. Since the attribute retrieval must use the exact same type, some care must be taken to ensure that the correct type is stored. E.g., be sure to construct std::string if you're passing a string literal and expect to read an std::string value back later, otherwise the attribute value will be of type const char*.

obj.setAttribute(DUCKS, 10);
obj.setAttribute(WINDOW, WindowPosition(100, 200));
obj.setAttribute(GRAPHVIZ, std::string("/dev/null"));

Since storing a value that can be interpreted as Boolean false is different than storing no value at all, the API has an attributeExists method that can be used to conditionally retrieve the value. If one calls getAttribute for an attribute that has not been stored in the object, then a DoesNotExist exception is thrown. If the attribute exists but the type parameter differs from the type of value that was stored, then a WrongQueryType exception is thrown.

int nDucks = obj.attributeExists(DUCKS) ? obj.getAttribute<int>(DUCKS) : 1;
WindowPosition win;
if (obj.attributeExists(WINDOW))
win = obj.getAttribute<WindowPosition>(WINDOW);
std::string fname;
if (obj.attributeExists(GRAPHVIZ))
fname = obj.getAttribute<std::string>(GRAPHVIZ);

The getAttribute method of retrieval can be a bit cumbersome since one has to check for existence first, so the API has additional methods that provide a default value. The default value is either specified as an argument, or instantiated with a default constructor.

nDucks = obj.attributeOrElse(DUCKS, 1); // stored ducks or 1
win = obj.attributeOrElse(WINDOW, WindowPosition());
fname = obj.attributeOrDefault<std::string>(GRAPHVIZ);

It's also possible to get a Sawyer::Optional value, an object that stores either the attribute value or nothing. One of the useful things with this approach is being able to check for existence and assign to a variable at the same time even if the attribute value could be interpreted as false:

if (obj.optionalAttribute<int>(DUCKS).assignTo(nDucks)) {
// reached even if nDucks == 0
} else {
// reached only if DUCKS attribute is not stored

Attributes can be erased from an object. Erasing an attribute value has no effect on which attribute ID numbers are registered in the global attribute symbol table.


Providing attribute storage capability

Providing the ability to store attributes in your own classes is easy: just inherit from Attribute::Storage. For a class hierarchy, only the base class should directly inherit from Attribute::Storage.

struct MyClass: public Sawyer::Attribute::Storage<> {
// additional members...

When such an object is assigned or copy-constructed the attribute values are copied using their assignment or copy constructors. When an object is deleted, its attribute values are also deleted.


class  AlreadyExists
 Exception thrown when redeclaring an existing attribute. More...
class  DoesNotExist
 Exception for non-existing values. More...
class  Storage
 API and storage for attributes. More...


typedef size_t Id
 Attribute identification. More...
typedef boost::bad_any_cast WrongQueryType
 Exception thrown when wrong data type is queried. More...


Id declare (const std::string &name)
 Register a new attribute key. More...
Id declareMaybe (const std::string &name)
 Register a new attribute key if not already registered. More...
Id id (const std::string &name)
 Returns the ID for an attribute name. More...
const std::string & name (Id)
 Returns the name for an attribute ID. More...


 Invalid attribute ID. More...

Typedef Documentation

typedef size_t Sawyer::Attribute::Id

Attribute identification.

Each attribute name has a unique identification number and vice versa.

Definition at line 140 of file Attribute.h.

typedef boost::bad_any_cast Sawyer::Attribute::WrongQueryType

Exception thrown when wrong data type is queried.

Definition at line 203 of file Attribute.h.

Function Documentation

Id Sawyer::Attribute::declare ( const std::string &  name)

Register a new attribute key.

The attribute name is registered with the system and an identifier is returned. The attribute name/ID association is stored in a single, global attribute symbol table. This method throws an AlreadyExists error if the specified name already exists in that global table. Once an attribute is registered its ID never changes and it is never removed from the global attribute symbol table. There is no guarantee that attribute ID numbers are small consecutive integers, although that is how the current implementation works.

Thread safety: This method is thread safe.

Id Sawyer::Attribute::declareMaybe ( const std::string &  name)

Register a new attribute key if not already registered.

If the name is already registered, then return its ID. Otherwise register the name and return a new ID.

Thread safety: This method is thread safe.

Id Sawyer::Attribute::id ( const std::string &  name)

Returns the ID for an attribute name.

Looks up the specified name in the global attribute symbol table and returns its identification number. Returns INVALID_ID if the name does not exist.

Thread safety: This method is thread safe.

const std::string& Sawyer::Attribute::name ( Id  )

Returns the name for an attribute ID.

Looks up the specified attribute ID in the global attribute symbol table and returns its name. Returns the empty string if the ID does not exist.

Thread safety: This method is thread safe.

Referenced by Sawyer::Attribute::Storage<>::getAttribute().

Variable Documentation

const Id Sawyer::Attribute::INVALID_ID

Invalid attribute ID.