doc/manual/source/attributes.rst
changeset 6742 a1759a95842c
child 6751 700a92acb8ec
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/manual/source/attributes.rst	Thu Dec 30 23:39:27 2010 +0100
@@ -0,0 +1,801 @@
+.. include:: replace.txt
+
+Attributes
+----------
+
+In |ns3| simulations, there are two main aspects to configuration:
+
+* the simulation topology and how objects are connected 
+* the values used by the models instantiated in the topology
+
+This chapter focuses on the second item above: how the many values in use in
+|ns3| are organized, documented, and modifiable by |ns3| users. The |ns3|
+attribute system is also the underpinning of how traces and statistics are
+gathered in the simulator. 
+
+Before delving into details of the attribute value system, it will help to
+review some basic properties of class :cpp:class:`ns3::Object`.
+
+Object Overview
+***************
+
+|ns3| is fundamentally a C++ object-based system. By this we mean that new C++
+classes (types) can be declared, defined, and subclassed as usual.
+
+Many |ns3| objects inherit from the :cpp:class:`ns3::Object` base class.  These
+objects have some additional properties that we exploit for organizing the
+system and improving the memory management of our objects:
+
+* a "metadata" system that links the class name to a lot of meta-information
+  about the object, including the base class of the subclass, the set of
+  accessible constructors in the subclass, and the set of "attributes" of the
+  subclass
+* a reference counting smart pointer implementation, for memory management.
+
+|ns3| objects that use the attribute system derive from either
+:cpp:class:`ns3::Object` or :cpp:class:`ns3::ObjectBase`. Most |ns3| objects we
+will discuss derive from :cpp:class:`ns3::Object`, but a few that are outside
+the smart pointer memory management framework derive from
+:cpp:class:`ns3::ObjectBase`.
+
+Let's review a couple of properties of these objects.
+
+Smart pointers
+**************
+
+As introduced in the |ns3| tutorial, |ns3| objects are memory managed by a
+`reference counting smart pointer implementation
+<http://en.wikipedia.org/wiki/Smart_pointer>`_, class :cpp:class:`ns3::Ptr`. 
+
+Smart pointers are used extensively in the |ns3| APIs, to avoid passing
+references to heap-allocated objects that may cause memory leaks.  
+For most basic usage (syntax), treat a smart pointer like a regular pointer:::
+
+  Ptr<WifiNetDevice> nd = ...;
+  nd->CallSomeFunction ();
+  // etc.
+
+CreateObject
+++++++++++++
+
+As we discussed above in :ref:`Memory management and class Ptr`, at the
+lowest-level API, objects of type :cpp:class:`ns3::Object` are not instantiated
+using ``operator new`` as usual but instead by a templated function called
+:cpp:func:`CreateObject()`.
+
+A typical way to create such an object is as follows:::
+
+  Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
+
+You can think of this as being functionally equivalent to:::
+
+  WifiNetDevice* nd = new WifiNetDevice ();
+
+Objects that derive from :cpp:class:`ns3::Object` must be allocated on the heap
+using CreateObject(). Those deriving from :cpp:class:`ns3::ObjectBase`, such as
+|ns3| helper functions and packet headers and trailers, can be allocated on the
+stack.  
+
+In some scripts, you may not see a lot of CreateObject() calls in the code; this
+is because there are some helper objects in effect that are doing the
+CreateObject()s for you.
+
+TypeId
+++++++
+
+|ns3| classes that derive from class ns3::Object can include a metadata class
+called ``TypeId`` that records meta-information about the class, for use in the
+object aggregation and component manager systems:
+
+* a unique string identifying the class
+* the base class of the subclass, within the metadata system
+* the set of accessible constructors in the subclass
+
+Object Summary
+++++++++++++++
+
+Putting all of these concepts together, let's look at a specific
+example: class :cpp:class:`ns3::Node`.
+
+The public header file node.h has a declaration that includes a static GetTypeId
+function call:::
+
+    class Node : public Object
+    {
+    public:
+      static TypeId GetTypeId (void);
+      ...
+
+This is defined in the ``node.cc`` file as follows:::
+
+    TypeId 
+    Node::GetTypeId (void)
+    {
+      static TypeId tid = TypeId ("ns3::Node")
+        .SetParent<Object> ()
+        .AddConstructor<Node> ()
+        .AddAttribute ("DeviceList", "The list of devices associated to this Node.",
+                       ObjectVectorValue (),
+                       MakeObjectVectorAccessor (&Node::m_devices),
+                       MakeObjectVectorChecker<NetDevice> ())
+        .AddAttribute ("ApplicationList", "The list of applications associated to this Node.",
+                       ObjectVectorValue (),
+                       MakeObjectVectorAccessor (&Node::m_applications),
+                       MakeObjectVectorChecker<Application> ())
+        .AddAttribute ("Id", "The id (unique integer) of this Node.",
+                       TypeId::ATTR_GET, // allow only getting it.
+                       UintegerValue (0),
+                       MakeUintegerAccessor (&Node::m_id),
+                       MakeUintegerChecker<uint32_t> ())
+        ;
+      return tid;
+    }
+
+Consider the TypeId of an |ns3| ``Object`` class as an extended form of run time
+type information (RTTI). The C++ language includes a simple kind of RTTI in
+order to support ``dynamic_cast`` and ``typeid`` operators.
+
+The "``.SetParent<Object> ()``" call in the declaration above is used in
+conjunction with our object aggregation mechanisms to allow safe up- and
+down-casting in inheritance trees during ``GetObject``.
+
+The "``.AddConstructor<Node> ()``" call is used in conjunction with our abstract
+object factory mechanisms to allow us to construct C++ objects without forcing a
+user to know the concrete class of the object she is building.
+
+The three calls to "``.AddAttribute``" associate a given string with a strongly
+typed value in the class. Notice that you must provide a help string which may
+be displayed, for example, via command line processors. Each ``Attribute`` is
+associated with mechanisms for accessing the underlying member variable in the
+object (for example, ``MakeUintegerAccessor`` tells the generic ``Attribute``
+code how to get to the node ID above). There are also "Checker" methods which
+are used to validate values.
+
+When users want to create Nodes, they will usually call some form of 
+``CreateObject``,::
+
+    Ptr<Node> n = CreateObject<Node> ();
+
+or more abstractly, using an object factory, you can create a ``Node`` object
+without even knowing the concrete C++ type::
+
+    ObjectFactory factory;
+    const std::string typeId = "ns3::Node'';
+    factory.SetTypeId (typeId);
+    Ptr<Object> node = factory.Create <Object> ();
+
+Both of these methods result in fully initialized attributes being available 
+in the resulting ``Object`` instances.
+
+We next discuss how attributes (values associated with member variables or
+functions of the class) are plumbed into the above TypeId.
+
+Attribute Overview
+******************
+
+The goal of the attribute system is to organize the access of
+internal member objects of a simulation. This goal arises because,
+typically in simulation, users will cut and paste/modify existing
+simulation scripts, or will use higher-level simulation constructs,
+but often will be interested in studying or tracing particular 
+internal variables.  For instance, use cases such as:
+
+* "I want to trace the packets on the wireless interface only on the first
+  access point"
+* "I want to trace the value of the TCP congestion window (every time it
+  changes) on a particular TCP socket"
+* "I want a dump of all values that were used in my simulation."
+
+Similarly, users may want fine-grained access to internal variables in the
+simulation, or may want to broadly change the initial value used for a
+particular parameter in all subsequently created objects. Finally, users may
+wish to know what variables are settable and retrievable in a simulation
+configuration. This is not just for direct simulation interaction on the command
+line; consider also a (future) graphical user interface that would like to be
+able to provide a feature whereby a user might right-click on an node on the
+canvas and see a hierarchical, organized list of parameters that are settable on
+the node and its constituent member objects, and help text and default values
+for each parameter.
+
+Functional overview
++++++++++++++++++++
+
+We provide a way for users to access values deep in the system, without having
+to plumb accessors (pointers) through the system and walk pointer chains to get
+to them. Consider a class DropTailQueue that has a member variable that is an
+unsigned integer ``m_maxPackets``; this member variable controls the depth of
+the queue.  
+
+If we look at the declaration of DropTailQueue, we see the following:::
+
+    class DropTailQueue : public Queue {
+    public:
+      static TypeId GetTypeId (void);
+      ...
+
+    private:
+      std::queue<Ptr<Packet> > m_packets;
+      uint32_t m_maxPackets;
+    };
+
+Let's consider things that a user may want to do with the value of
+m_maxPackets:
+
+* Set a default value for the system, such that whenever a new DropTailQueue is
+  created, this member is initialized to that default. 
+* Set or get the value on an already instantiated queue.
+
+The above things typically require providing Set() and Get() functions, and some
+type of global default value.
+
+In the |ns3| attribute system, these value definitions and accessor functions
+are moved into the TypeId class; e.g.:::
+
+    NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
+
+    TypeId DropTailQueue::GetTypeId (void) 
+    {
+      static TypeId tid = TypeId ("ns3::DropTailQueue")
+        .SetParent<Queue> ()
+        .AddConstructor<DropTailQueue> ()
+        .AddAttribute ("MaxPackets", 
+                       "The maximum number of packets accepted by this DropTailQueue.",
+                       UintegerValue (100),
+                       MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
+                       MakeUintegerChecker<uint32_t> ())
+        ;
+      
+      return tid;
+    }
+
+The AddAttribute() method is performing a number of things with this
+value:
+
+* Binding the variable m_maxPackets to a string "MaxPackets"
+* Providing a default value (100 packets)
+* Providing some help text defining the value
+* Providing a "checker" (not used in this example) that can be used to set
+  bounds on the allowable range of values
+
+The key point is that now the value of this variable and its default value are
+accessible in the attribute namespace, which is based on strings such as
+"MaxPackets" and TypeId strings. In the next section, we will provide an example
+script that shows how users may manipulate these values.
+
+Note that initialization of the attribute relies on the macro
+``NS_OBJECT_ENSURE_REGISTERED`` (DropTailQueue) being called; if you leave this
+out of your new class implementation, your attributes will not be initialized
+correctly.
+
+While we have described how to create attributes, we still haven't described how
+to access and manage these values. For instance, there is no ``globals.h``
+header file where these are stored; attributes are stored with their classes.
+Questions that naturally arise are how do users easily learn about all of the
+attributes of their models, and how does a user access these attributes, or
+document their values as part of the record of their simulation?
+
+Default values and command-line arguments
++++++++++++++++++++++++++++++++++++++++++
+
+Let's look at how a user script might access these values.  
+This is based on the script found at ``samples/main-attribute-value.cc``,
+with some details stripped out.::
+
+    //
+    // This is a basic example of how to use the attribute system to
+    // set and get a value in the underlying system; namely, an unsigned
+    // integer of the maximum number of packets in a queue
+    //
+
+    int 
+    main (int argc, char *argv[])
+    {
+
+      // By default, the MaxPackets attribute has a value of 100 packets
+      // (this default can be observed in the function DropTailQueue::GetTypeId)
+      // 
+      // Here, we set it to 80 packets.  We could use one of two value types:
+      // a string-based value or a Uinteger value
+      Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
+      // The below function call is redundant
+      Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
+
+      // Allow the user to override any of the defaults and the above
+      // SetDefaults() at run-time, via command-line arguments
+      CommandLine cmd;
+      cmd.Parse (argc, argv);
+
+The main thing to notice in the above are the two calls to 
+``Config::SetDefault``.  This is how we set the default value
+for all subsequently instantiated DropTailQueues.  We illustrate
+that two types of Value classes, a StringValue and a UintegerValue class,
+can be used to assign the value to the attribute named by
+"ns3::DropTailQueue::MaxPackets".
+
+Now, we will create a few objects using the low-level API; here,
+our newly created queues will not have a m_maxPackets initialized to
+100 packets but to 80 packets, because of what we did above with
+default values.::
+
+    Ptr<Node> n0 = CreateObject<Node> ();
+
+    Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
+    n0->AddDevice (net0);
+
+    Ptr<Queue> q = CreateObject<DropTailQueue> ();
+    net0->AddQueue(q);
+
+At this point, we have created a single node (Node 0) and a single
+PointToPointNetDevice (NetDevice 0) and added a DropTailQueue to it.
+
+Now, we can manipulate the MaxPackets value of the already instantiated
+DropTailQueue. Here are various ways to do that.
+
+Pointer-based access
+++++++++++++++++++++
+
+We assume that a smart pointer (Ptr) to a relevant network device is in hand; in
+the current example, it is the ``net0`` pointer. 
+
+One way to change the value is to access a pointer to the underlying queue and
+modify its attribute.
+ 
+First, we observe that we can get a pointer to the (base class) queue via the
+PointToPointNetDevice attributes, where it is called TxQueue::
+
+    PointerValue tmp;
+    net0->GetAttribute ("TxQueue", tmp);
+    Ptr<Object> txQueue = tmp.GetObject ();
+
+Using the GetObject function, we can perform a safe downcast to a DropTailQueue,
+where MaxPackets is a member::
+
+    Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
+    NS_ASSERT (dtq != 0);
+
+Next, we can get the value of an attribute on this queue.  We have introduced
+wrapper "Value" classes for the underlying data types, similar to Java wrappers
+around these types, since the attribute system stores values and not disparate
+types.  Here, the attribute value is assigned to a UintegerValue, and the Get()
+method on this value produces the (unwrapped) uint32_t.::
+
+    UintegerValue limit;
+    dtq->GetAttribute ("MaxPackets", limit);
+    NS_LOG_INFO ("1.  dtq limit: " << limit.Get () << " packets");
+  
+Note that the above downcast is not really needed; we could have done the same
+using the Ptr<Queue> even though the attribute is a member of the subclass::
+
+    txQueue->GetAttribute ("MaxPackets", limit);
+    NS_LOG_INFO ("2.  txQueue limit: " << limit.Get () << " packets");
+
+Now, let's set it to another value (60 packets)::
+
+    txQueue->SetAttribute("MaxPackets", UintegerValue (60));
+    txQueue->GetAttribute ("MaxPackets", limit);
+    NS_LOG_INFO ("3.  txQueue limit changed: " << limit.Get () << " packets");
+
+Namespace-based access
+++++++++++++++++++++++
+
+An alternative way to get at the attribute is to use the configuration
+namespace.  Here, this attribute resides on a known path in this namespace; this
+approach is useful if one doesn't have access to the underlying pointers and
+would like to configure a specific attribute with a single statement.::
+
+    Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue (25));
+    txQueue->GetAttribute ("MaxPackets", limit); 
+    NS_LOG_INFO ("4.  txQueue limit changed through namespace: " << 
+        limit.Get () << " packets");
+
+We could have also used wildcards to set this value for all nodes and all net
+devices (which in this simple example has the same effect as the previous
+Set())::
+
+    Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue (15));
+    txQueue->GetAttribute ("MaxPackets", limit); 
+    NS_LOG_INFO ("5.  txQueue limit changed through wildcarded namespace: " << 
+        limit.Get () << " packets");
+
+Object Name Service-based access
+++++++++++++++++++++++++++++++++
+
+Another way to get at the attribute is to use the object name service facility.
+Here, this attribute is found using a name string. This approach is useful if
+one doesn't have access to the underlying pointers and it is difficult to
+determine the required concrete configuration namespaced path.::
+
+    Names::Add ("server", serverNode);
+    Names::Add ("server/eth0", serverDevice);
+
+    ...
+
+    Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
+
+:ref:`Object names` for a fuller treatment of the |ns3| configuration namespace.
+
+Setting through constructors helper classes
++++++++++++++++++++++++++++++++++++++++++++
+
+Arbitrary combinations of attributes can be set and fetched from
+the helper and low-level APIs; either from the constructors themselves:::
+
+    Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
+
+or from the higher-level helper APIs, such as:::
+
+    mobility.SetPositionAllocator ("GridPositionAllocator",
+                                   "MinX", DoubleValue (-100.0),
+                                   "MinY", DoubleValue (-100.0),
+                                   "DeltaX", DoubleValue (5.0),
+                                   "DeltaY", DoubleValue (20.0),
+                                   "GridWidth", UintegerValue (20),
+                                   "LayoutType", StringValue ("RowFirst"));
+
+Implementation details
+++++++++++++++++++++++
+
+Value classes
+~~~~~~~~~~~~~
+
+Readers will note the new FooValue classes which are subclasses of the
+AttributeValue base class. These can be thought of as an intermediate class that
+can be used to convert from raw types to the Values that are used by the
+attribute system. Recall that this database is holding objects of many types
+with a single generic type. Conversions to this type can either be done using an
+intermediate class (IntegerValue, DoubleValue for "floating point") or via
+strings. Direct implicit conversion of types to Value is not really practical.
+So in the above, users have a choice of using strings or values:::
+
+    p->Set ("cwnd", StringValue ("100")); // string-based setter
+    p->Set ("cwnd", IntegerValue (100)); // integer-based setter
+
+The system provides some macros that help users declare and define
+new AttributeValue subclasses for new types that they want to introduce into
+the attribute system: 
+
+* ATTRIBUTE_HELPER_HEADER
+* ATTRIBUTE_HELPER_CPP
+
+Initialization order
+~~~~~~~~~~~~~~~~~~~~
+
+Attributes in the system must not depend on the state of any other Attribute in
+this system. This is because an ordering of Attribute initialization is not
+specified, nor enforced, by the system. A specific example of this can be seen
+in automated configuration programs such as :cpp:class:`ns3::ConfigStore`.
+Although a given model may arrange it so that Attributes are initialized in a
+particular order, another automatic configurator may decide independently to
+change Attributes in, for example, alphabetic order.  
+
+Because of this non-specific ordering, no Attribute in the system may have any
+dependence on any other Attribute. As a corollary, Attribute setters must never
+fail due to the state of another Attribute. No Attribute setter may change (set)
+any other Attribute value as a result of changing its value.
+
+This is a very strong restriction and there are cases where Attributes must set
+consistently to allow correct operation. To this end we do allow for consistency
+checking *when the attribute is used* (cf. NS_ASSERT_MSG or NS_ABORT_MSG).
+
+In general, the attribute code to assign values to the underlying class member
+variables is executed after an object is constructed. But what if you need the
+values assigned before the constructor body executes, because you need them in
+the logic of the constructor? There is a way to do this, used for example in the
+class :cpp:class:`ns3::ConfigStore`: call ``ObjectBase::ConstructSelf ()`` as
+follows:::
+
+    ConfigStore::ConfigStore ()
+    {
+      ObjectBase::ConstructSelf (AttributeList ());
+      // continue on with constructor.
+    }
+
+Extending attributes
+********************
+
+The |ns3| system will place a number of internal values under the attribute
+system, but undoubtedly users will want to extend this to pick up ones we have
+missed, or to add their own classes to this.
+
+Adding an existing internal variable to the metadata system 
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Consider this variable in class TcpSocket:::
+
+    uint32_t m_cWnd;   // Congestion window
+
+Suppose that someone working with TCP wanted to get or set the value of that
+variable using the metadata system. If it were not already provided by |ns3|,
+the user could declare the following addition in the runtime metadata system (to
+the TypeId declaration for TcpSocket):::
+
+    .AddAttribute ("Congestion window", 
+                   "Tcp congestion window (bytes)",
+                   UintegerValue (1),
+                   MakeUintegerAccessor (&TcpSocket::m_cWnd),
+                   MakeUintegerChecker<uint16_t> ())
+
+Now, the user with a pointer to the TcpSocket can perform operations such as
+setting and getting the value, without having to add these functions explicitly.
+Furthermore, access controls can be applied, such as allowing the parameter to
+be read and not written, or bounds checking on the permissible values can be
+applied.
+
+Adding a new TypeId
++++++++++++++++++++
+
+Here, we discuss the impact on a user who wants to add a new class to |ns3|;
+what additional things must be done to hook it into this system.
+
+We've already introduced what a TypeId definition looks like:::
+
+    TypeId
+    RandomWalk2dMobilityModel::GetTypeId (void)
+    {
+      static TypeId tid = TypeId ("ns3::RandomWalk2dMobilityModel")
+        .SetParent<MobilityModel> ()
+        .SetGroupName ("Mobility")
+        .AddConstructor<RandomWalk2dMobilityModel> ()
+        .AddAttribute ("Bounds",
+                       "Bounds of the area to cruise.",
+                       RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
+                       MakeRectangleAccessor (&RandomWalk2dMobilityModel::m_bounds),
+                       MakeRectangleChecker ())
+        .AddAttribute ("Time",
+                       "Change current direction and speed after moving for this delay.",
+                       TimeValue (Seconds (1.0)),
+                       MakeTimeAccessor (&RandomWalk2dMobilityModel::m_modeTime),
+                       MakeTimeChecker ())
+        // etc (more parameters).
+        ;
+      return tid;
+    }
+
+The declaration for this in the class declaration is one-line public member
+method:::
+
+    public:
+      static TypeId GetTypeId (void);
+
+Typical mistakes here involve:
+
+* Not calling the SetParent method or calling it with the wrong type
+* Not calling the AddConstructor method of calling it with the wrong type
+* Introducing a typographical error in the name of the TypeId in its constructor
+* Not using the fully-qualified c++ typename of the enclosing c++ class as the
+  name of the TypeId
+
+None of these mistakes can be detected by the |ns3| codebase so, users
+are advised to check carefully multiple times that they got these right.
+
+Adding new class type to the attribute system
+*********************************************
+
+From the perspective of the user who writes a new class in the system and wants
+to hook it in to the attribute system, there is mainly the matter of writing the
+conversions to/from strings and attribute values.  Most of this can be
+copy/pasted with macro-ized code.  For instance, consider class declaration for
+Rectangle in the ``src/mobility/`` directory:
+
+Header file
++++++++++++
+
+::
+
+    /**
+     * \brief a 2d rectangle
+     */
+    class Rectangle
+    {
+      ...
+
+      double xMin;
+      double xMax;
+      double yMin;
+      double yMax;
+    };
+ 
+One macro call and two operators, must be added below the class declaration in
+order to turn a Rectangle into a value usable by the ``Attribute`` system:::
+
+    std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
+    std::istream &operator >> (std::istream &is, Rectangle &rectangle);
+
+    ATTRIBUTE_HELPER_HEADER (Rectangle);
+
+Implementation file
++++++++++++++++++++
+
+In the class definition (``.cc`` file), the code looks like this:::
+
+    ATTRIBUTE_HELPER_CPP (Rectangle);
+
+    std::ostream &
+    operator << (std::ostream &os, const Rectangle &rectangle)
+    {
+      os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|"
+         << rectangle.yMax;
+      return os;
+    }
+    std::istream &
+    operator >> (std::istream &is, Rectangle &rectangle)
+     {
+      char c1, c2, c3;
+      is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 
+         >> rectangle.yMax;
+      if (c1 != '|' ||
+          c2 != '|' ||
+          c3 != '|')
+        {
+          is.setstate (std::ios_base::failbit);
+        }
+      return is;
+    }
+
+These stream operators simply convert from a string representation of the
+Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the modeler
+must specify these operators and the string syntactical representation of an
+instance of the new class.
+
+ConfigStore
+***********
+
+**Feedback requested:**  This is an experimental feature of |ns3|.  It is found
+in ``src/contrib`` and not in the main tree.  If you like this feature and
+would like to provide feedback on it, please email us.
+
+Values for |ns3| attributes can be stored in an ASCII or XML text file and
+loaded into a future simulation.  This feature is known as the |ns3|
+ConfigStore.  The ConfigStore code is in ``src/contrib/``.  It is not yet
+main-tree code, because we are seeking some user feedback and experience with
+this. 
+
+We can explore this system by using an example. Copy the ``csma-bridge.cc``
+file to the scratch directory:::
+
+    cp examples/csma-bridge.cc scratch/
+    ./waf
+
+Let's edit it to add the ConfigStore feature. First, add an include statement to
+include the contrib module, and then add these lines:::
+
+    #include "contrib-module.h"
+    ...
+    int main (...)
+    {
+      // setup topology
+
+      // Invoke just before entering Simulator::Run ()
+      ConfigStore config;
+      config.ConfigureDefaults ();
+      config.ConfigureAttributes ();
+
+      Simulator::Run ();
+    }
+
+There are three attributes that govern the behavior of the ConfigStore: "Mode",
+"Filename", and "FileFormat".  The Mode (default "None") configures whether
+|ns3| should load configuration from a previously saved file (specify
+"Mode=Load") or save it to a file (specify "Mode=Save").  The Filename (default
+"") is where the ConfigStore should store its output data.  The FileFormat
+(default "RawText") governs whether the ConfigStore format is Xml or RawText
+format.
+
+So, using the above modified program, try executing the following waf command
+and ::
+
+    ./waf --command-template="%s --ns3::ConfigStore::Filename=csma-bridge-config.xml
+    --ns3::ConfigStore::Mode=Save --ns3::ConfigStore::FileFormat=Xml" --run scratch/csma-bridge
+    @end example
+    @end smallformat
+    After running, you can open the csma-bridge-config.xml file and it will
+    display the configuration that was applied to your simulation; e.g.
+    @smallformat
+    @example
+    <?xml version="1.0" encoding="UTF-8"?>
+    <ns3>
+     <default name="ns3::V4Ping::Remote" value="102.102.102.102"/>
+     <default name="ns3::MsduStandardAggregator::MaxAmsduSize" value="7935"/>
+     <default name="ns3::EdcaTxopN::MinCw" value="31"/>
+     <default name="ns3::EdcaTxopN::MaxCw" value="1023"/>
+     <default name="ns3::EdcaTxopN::Aifsn" value="3"/>
+     <default name="ns3::QstaWifiMac::ProbeRequestTimeout" value="50000000ns"/>
+     <default name="ns3::QstaWifiMac::AssocRequestTimeout" value="500000000ns"/>
+     <default name="ns3::QstaWifiMac::MaxMissedBeacons" value="10"/>
+     <default name="ns3::QstaWifiMac::ActiveProbing" value="false"/>
+    ...
+
+This file can be archived with your simulation script and output data.
+
+While it is possible to generate a sample config file and lightly edit it to
+change a couple of values, there are cases where this process will not work
+because the same value on the same object can appear multiple times in the same
+automatically-generated configuration file under different configuration paths.
+
+As such, the best way to use this class is to use it to generate an initial
+configuration file, extract from that configuration file only the strictly
+necessary elements, and move these minimal elements to a new configuration file
+which can then safely be edited and loaded in a subsequent simulation run. 
+
+When the ConfigStore object is instantiated, its attributes Filename, Mode, and
+FileFormat must be set, either via command-line or via program statements.  
+
+As a more complicated example, let's assume that we want to read in a
+configuration of defaults from an input file named "input-defaults.xml", and
+write out the resulting attributes to a separate file called
+"output-attributes.xml".  (Note-- to get this input xml file to begin with, it
+is sometimes helpful to run the program to generate an output xml file first,
+then hand-edit that file and re-input it for the next simulation run).::
+
+    #include "contrib-module.h"
+    ...
+    int main (...)
+    {
+
+      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
+      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
+      Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
+      ConfigStore inputConfig;
+      inputConfig.ConfigureDefaults ();
+      
+      //
+      // Allow the user to override any of the defaults and the above Bind() at
+      // run-time, via command-line arguments
+      //
+      CommandLine cmd;
+      cmd.Parse (argc, argv);
+
+      // setup topology
+      ...
+
+      // Invoke just before entering Simulator::Run ()
+      Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
+      Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
+      ConfigStore outputConfig;
+      outputConfig.ConfigureAttributes ();
+      Simulator::Run ();
+    }
+
+GTK-based ConfigStore
++++++++++++++++++++++
+
+There is a GTK-based front end for the ConfigStore.  This allows users to use a
+GUI to access and change variables.  Screenshots of this feature are available
+in the `|ns3| Overview <http://www.nsnam.org/docs/ns-3-overview.pdf>`_
+presentation.
+
+To use this feature, one must install libgtk and libgtk-dev; an example
+Ubuntu installation command is:::
+
+    sudo apt-get install libgtk2.0-0 libgtk2.0-dev
+
+To check whether it is configured or not, check the output of the
+./waf configure step:::
+
+    ---- Summary of optional NS-3 features:
+    Threading Primitives          : enabled
+    Real Time Simulator           : enabled
+    GtkConfigStore                : not enabled (library 'gtk+-2.0 >= 2.12' not found)
+
+In the above example, it was not enabled, so it cannot be used until a suitable
+version is installed and ./waf configure; ./waf is rerun.
+
+Usage is almost the same as the non-GTK-based version, but there
+are no ConfigStore attributes involved:::
+
+  // Invoke just before entering Simulator::Run ()
+  GtkConfigStore config;
+  config.ConfigureDefaults ();
+  config.ConfigureAttributes ();
+
+Now, when you run the script, a GUI should pop up, allowing you to open menus of
+attributes on different nodes/objects, and then launch the simulation execution
+when you are done.  
+
+Future work
++++++++++++
+There are a couple of possible improvements:
+* save a unique version number with date and time at start of file
+* save rng initial seed somewhere.
+* make each RandomVariable serialize its own initial seed and re-read it later
+* add the default values