Summary
In closing, let's revisit my original list of requirements
for the Attribute library:
-
The
library does not require sweeping changes to existing types or methods.
-
The
library is generic and supports complex types. Furthermore, it allows for full
control over output through partial template specialization.
-
While
the memory footprint is larger than I would have liked, it is manageable and
easily modified to decrease the overall size. I mentioned using Proxy objects
above. Proxy objects can be the answer to the memory problem, swapping run-time
processing for memory use.
-
Attribute
behaviors are registered at the time the Attribute is bound to the
AttributeContainer. These behaviors can be obtained and modified procedurally.
-
The
AttributeContainer provides a mechanism to retrieve all of the bound Attribute names
and values. This aids in exposing attributes to user interfaces and scripts.
-
Serialization
is accomplished through the AttributeContainerIO policy class and is limited in
automation only by the implementation of this policy.
-
Attribute
values can be modified indirectly through the SetValue() method.
-
Since
Attributes are nothing more than named keys to the underlying memory address of
the variable, any change to that variable is immediately visible to the
Attribute class.
To be fair, I agonized over writing this article. Attribute and AttributeManager, while useful,
have their limitations as well. They can be a great aid in simplifying I/O and
are tremendously useful in exposing variables to scripting engines and user
interfaces, but this functionality comes at a cost.
Used wisely, this library
can save you a great deal of time and simplify debugging efforts. Use it
without thought, and it may quickly spiral into a resource nightmare.
Note: Alongside the article, we
present zip files which contain all of the article code in two formats. The
first zip file contains only the code necessary for the library and full
documentation. The second file contains all of the first zip, but it also
contains a visual studio solution file with several examples and unit test code
for verifying that the library does what it says it does.
Though the versions hosted by Gamasutra are current as of this posting, the author's website hosts these files as well; any updates that may be made to these files will be reflected in these versions: file 1, file 2.
For More Information
Design Patterns:
Elements of Reusable Object-Oriented Software
ISBN: 0201633612
By Erich Gamma, Richard Helm, Ralph Johnson, and John M. Vlissides (Nov 10, 1994)
Effective STL: 50 Specific Ways to Improve Your Use of the Standard
Template Library
ISBN: 0201749629
by Scott Meyers (Jun 16, 2001)
I should point out that this is but one of many different
implementations. Several authors have
implemented similar libraries. Here are
two that I know of:
The Boost Fusion
library: http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/index.html
The fusion library from boost is, like all things boost,
complex and wrought with deep interdependencies on numerous other boost
components. I like boost as a testbed,
but its complexity and sheer size far outweigh its usefulness in real world
applications.
Property class for
Generic C++ Member Access, by Charles Cafrelli
Game Gems, Book 1. http://www.charlesriver.com/Books/BookDetail.aspx?productID=8714
The property class implemented by Mr. Cafrelli was the
inspiration for this library. I wanted
an implementation that was truly and fully generic, one that did not
necessarily require a code change to support every new type.
|
After rereading the article, I see you mentioned this refactor potential:
"One alternative is to have a global instance of AttributeContainer (or at least one that is in a larger scope than the objects it manages). While this saves some memory, it does complicate use, as each bound attribute must be somehow uniquely identified per object."
There are certainly some not-too-difficult solutions that simplify the global shared container pattern for client code.
In a system I worked with, the following client code would serialize an object's properties to a stream:
Object *pObject = ...;
MutableString mutstr;
StringOutputStream sos(mutstr);
XmlSerializer ser(os);
ser.Serialize(pObject);