doc/manual/attributes.texi
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Fri, 10 Dec 2010 13:47:30 +0100
changeset 6680 0e81f526bd49
parent 6673 ec22aa763e2d
permissions -rw-r--r--
get rid of texi2html warnings

@node Attributes
@chapter Attributes 
@anchor{chap:Attributes}

@menu
* Object Overview::::
* Attribute Overview::
* Extending attributes::
* Adding new class type::
* ConfigStore::
@end menu


In ns-3 simulations, there are two main aspects to configuration:
@itemize @bullet
@item the simulation topology and how objects are connected 
@item the values used by the models instantiated in the topology
@end itemize

This chapter focuses on the second item above: how the many values
in use in ns-3 are organized, documented, and modifiable by ns-3 users.
The ns-3 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 @code{class ns3::Object}.

@node Object Overview
@section Object Overview

ns-3 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 ns-3 objects inherit from the @code{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:

@itemize @bullet
@item 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
@item a reference counting smart pointer implementation, for memory
management.
@end itemize

ns-3 objects that use the attribute system derive from either
@code{ns3::Object} or @code{ns3::ObjectBase}.  Most ns-3 objects
we will discuss derive from @code{ns3::Object}, but a few that
are outside the smart pointer memory management framework derive
from @code{ns3::ObjectBase}.

Let's review a couple of properties of these objects.

@subsection Smart pointers

As introduced in the ns-3 tutorial, ns-3 objects are memory managed by a 
@uref{http://en.wikipedia.org/wiki/Smart_pointer,,reference counting smart pointer implementation}, @code{class ns3::Ptr}. 

Smart pointers are used extensively in the ns-3 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:
@verbatim
  Ptr<WifiNetDevice> nd = ...;
  nd->CallSomeFunction ();
  // etc.
@end verbatim

@subsection CreateObject

As we discussed above in @ref{Memory management and class Ptr},
at the lowest-level API, objects of type @code{ns3::Object} are 
not instantiated using @code{operator new} as usual but instead by
a templated function called @code{CreateObject()}.

A typical way to create such an object is as follows:
@verbatim
  Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
@end verbatim

You can think of this as being functionally equivalent to:
@verbatim
  WifiNetDevice* nd = new WifiNetDevice ();
@end verbatim

Objects that derive from @code{ns3::Object} must be allocated
on the heap using CreateObject().  Those deriving from 
@code{ns3::ObjectBase}, such as ns-3 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.

@subsection TypeId

ns-3 classes that derive from class ns3::Object can include
a metadata class called @code{TypeId} that records meta-information
about the class, for use in the object aggregation and component
manager systems:
@itemize @bullet
 @item a unique string identifying the class
 @item the base class of the subclass, within the metadata system
 @item the set of accessible constructors in the subclass
@end itemize

@subsection Object Summary

Putting all of these concepts together, let's look at a specific
example:  @code{class ns3::Node}.

The public header file node.h has a declaration that includes
a static GetTypeId function call:

@verbatim
class Node : public Object
@{
public:
  static TypeId GetTypeId (void);
  ...
@end verbatim

This is defined in the node.cc file as follows:

@smallformat
@example
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;
@}
@end example
@end smallformat

Consider the TypeId of an ns-3 @code{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 @code{dynamic_cast} and @code{typeid} operators.

The ``@code{.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 @code{GetObject}.

The ``@code{.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 ``@code{.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 
@code{Attribute} is associated with mechanisms for accessing the underlying
member variable in the object (for example, @code{MakeUintegerAccessor} tells
the generic @code{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 
@code{CreateObject},

@verbatim
  Ptr<Node> n = CreateObject<Node> ();
@end verbatim

or more abstractly, using an object factory, you can create a @code{Node} object
without even knowing the concrete C++ type

@verbatim
  ObjectFactory factory;
  const std::string typeId = "ns3::Node'';
  factory.SetTypeId (typeId);
  Ptr<Object> node = factory.Create <Object> ();
@end verbatim

Both of these methods result in fully initialized attributes being available 
in the resulting @code{Object} instances.

We next discuss how attributes (values associated with member variables
or functions of the class) are plumbed into the above TypeId.

@node Attribute Overview
@section 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:
@itemize @bullet
@item "I want to trace the packets on the wireless interface only on
the first access point"
@item "I want to trace the value of the TCP congestion window (every
time it changes) on a particular TCP socket"
@item "I want a dump of all values that were used in my simulation."
@end itemize 

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.

@subsection 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 @code{m_maxPackets};
this member variable controls the depth of the queue.  

If we look at the declaration of DropTailQueue, we see the following:
@verbatim
class DropTailQueue : public Queue {
public:
  static TypeId GetTypeId (void);
  ...

private:
  std::queue<Ptr<Packet> > m_packets;
  uint32_t m_maxPackets;
};
@end verbatim

Let's consider things that a user may want to do with the value of
m_maxPackets:

@itemize @bullet
@item Set a default value for the system, such that whenever a new
DropTailQueue is created, this member is initialized to that default. 
@item Set or get the value on an already instantiated queue.
@end itemize

The above things typically require providing Set() and Get() functions,
and some type of global default value.

In the ns-3 attribute system, these value definitions and accessor
functions are moved into the TypeId class; e.g.:  

@smallformat
@example
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;
@}
@end example
@end smallformat

The AddAttribute() method is performing a number of things with this
value:
@itemize @bullet
@item Binding the variable m_maxPackets to a string "MaxPackets"
@item Providing a default value (100 packets)
@item Providing some help text defining the value
@item Providing a "checker" (not used in this example) that can be used to set
bounds on the allowable range of values
@end itemize

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
@code{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 
@code{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?

@subsection 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 @code{samples/main-attribute-value.cc},
with some details stripped out.
@verbatim
//
// 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);
@end verbatim

The main thing to notice in the above are the two calls to 
@code{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.
@smallformat
@example
  Ptr<Node> n0 = CreateObject<Node> ();

  Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
  n0->AddDevice (net0);

  Ptr<Queue> q = CreateObject<DropTailQueue> ();
  net0->AddQueue(q);
@end example
@end smallformat

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.

@subsection 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 @code{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 
@example
  PointerValue tmp;
  net0->GetAttribute ("TxQueue", tmp);
  Ptr<Object> txQueue = tmp.GetObject ();
@end example

Using the GetObject function, we can perform a safe downcast
to a DropTailQueue, where MaxPackets is a member
@verbatim
  Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
  NS_ASSERT (dtq != 0);
@end verbatim

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.
@verbatim
  UintegerValue limit;
  dtq->GetAttribute ("MaxPackets", limit);
  NS_LOG_INFO ("1.  dtq limit: " << limit.Get () << " packets");
@end verbatim
  
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
@verbatim
  txQueue->GetAttribute ("MaxPackets", limit);
  NS_LOG_INFO ("2.  txQueue limit: " << limit.Get () << " packets");
@end verbatim

Now, let's set it to another value (60 packets)
@verbatim
  txQueue->SetAttribute("MaxPackets", UintegerValue (60));
  txQueue->GetAttribute ("MaxPackets", limit);
  NS_LOG_INFO ("3.  txQueue limit changed: " << limit.Get () << " packets");
@end verbatim

@subsection 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.  

@smallformat
@example
  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");
@end example
@end smallformat

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())
@smallformat
@example
  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");
@end example
@end smallformat

@subsection 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.

@smallformat
@example
  Names::Add ("server", serverNode);
  Names::Add ("server/eth0", serverDevice);

  ...

  Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
@end example
@end smallformat

@xref{Object names} for a fuller treatment of the ns-3 configuration namespace.

@subsection 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:
@verbatim
Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
@end verbatim
or from the higher-level helper APIs, such as:
@verbatim
  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"));
@end verbatim

@subsection Implementation details
@subsubsection 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: 
@verbatim
p->Set ("cwnd", StringValue ("100")); // string-based setter
p->Set ("cwnd", IntegerValue (100)); // integer-based setter
@end verbatim

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: 
@itemize @bullet
@item ATTRIBUTE_HELPER_HEADER
@item ATTRIBUTE_HELPER_CPP
@end itemize

@subsubsection 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 @code{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 @emph{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 
@code{ns3::ConfigStore}:  call @code{ObjectBase::ConstructSelf ()}
as follows:

@verbatim
ConfigStore::ConfigStore ()
@{
  ObjectBase::ConstructSelf (AttributeList ());
  // continue on with constructor.
@}
@end verbatim

@node Extending attributes
@section Extending attributes

The ns-3 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.

@subsection Adding an existing internal variable to the metadata system 

Consider this variable in class TcpSocket:
@verbatim
 uint32_t m_cWnd;   // Congestion window
@end verbatim

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 ns-3, the user could declare the following addition 
in the runtime metadata system (to the TypeId declaration for TcpSocket):
@verbatim
    .AddAttribute ("Congestion window", 
                   "Tcp congestion window (bytes)",
                   UintegerValue (1),
                   MakeUintegerAccessor (&TcpSocket::m_cWnd),
                   MakeUintegerChecker<uint16_t> ())

@end verbatim

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.

@subsection Adding a new TypeId

Here, we discuss the impact on a user who wants to add a new class to
ns-3; what additional things must be done to hook it into this system.

We've already introduced what a TypeId definition looks like:
@smallformat
@example
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;
@}
@end example
@end smallformat

The declaration for this in the class declaration is one-line public
member method:
@verbatim
 public:
  static TypeId GetTypeId (void);
@end verbatim

Typical mistakes here involve:
@itemize @bullet
@item Not calling the SetParent method or calling it with the wrong type
@item Not calling the AddConstructor method of calling it with the wrong type
@item Introducing a typographical error in the name of the TypeId in its constructor
@item Not using the fully-qualified c++ typename of the enclosing c++ class as the 
name of the TypeId
@end itemize
None of these mistakes can be detected by the ns-3 codebase so, users
are advised to check carefully multiple times that they got these right.


@node Adding new class type
@section 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 @code{src/mobility/} directory:

@subsection Header file
@smallformat
@example
/**
 * \brief a 2d rectangle
 */
class Rectangle
@{
  ...

  double xMin;
  double xMax;
  double yMin;
  double yMax;
@};
@end example
@end smallformat
 
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 @code{Attribute}
system:

@smallformat
@example
std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
std::istream &operator >> (std::istream &is, Rectangle &rectangle);

ATTRIBUTE_HELPER_HEADER (Rectangle);
@end example
@end smallformat

@subsection Implementation file
In the class definition (@code{.cc} file), the code looks like this:

@smallformat
@example
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;
@}
@end example
@end smallformat

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.

@node ConfigStore
@section ConfigStore

@strong{Feedback requested:}  This is an experimental feature of ns-3.  It is 
found in @code{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 ns-3 attributes can be stored in an ASCII or XML text file and
loaded into a future simulation.  This feature is known as the
ns-3 ConfigStore.  
The ConfigStore code is in @code{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 @code{csma-bridge.cc}
file to the scratch directory:
@verbatim
  cp examples/csma-bridge.cc scratch/
  ./waf
@end verbatim

Let's edit it to add the ConfigStore feature.  First, add an include statement 
to include the contrib module, and then add these lines:

@smallformat
@example
#include "contrib-module.h"
...
int main (...)
@{
  // setup topology

  // Invoke just before entering Simulator::Run ()
  ConfigStore config;
  config.ConfigureDefaults ();
  config.ConfigureAttributes ();

  Simulator::Run ();
@}
@end example
@end smallformat

There are three attributes that govern the behavior of the ConfigStore:
"Mode", "Filename", and "FileFormat".  The Mode (default "None") configures
whether ns-3 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 
@smallformat
@example
./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::StaWifiMac::ProbeRequestTimeout" value="50000000ns"/>
 <default name="ns3::StaWifiMac::AssocRequestTimeout" value="500000000ns"/>
 <default name="ns3::StaWifiMac::MaxMissedBeacons" value="10"/>
 <default name="ns3::StaWifiMac::ActiveProbing" value="false"/>
...
@end example
@end smallformat

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).
@smallformat
@example
#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 ();
@}
@end example
@end smallformat

@subsection 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 
@uref{http://www.nsnam.org/docs/ns-3-overview.pdf,,ns-3 Overview} presentation.

To use this feature, one must install libgtk and libgtk-dev; an example
Ubuntu installation command is:
@verbatim
sudo apt-get install libgtk2.0-0 libgtk2.0-dev
@end verbatim
To check whether it is configured or not, check the output of the
./waf configure step:
@smallformat
@example
---- Summary of optional NS-3 features:
Threading Primitives          : enabled
Real Time Simulator           : enabled
GtkConfigStore                : not enabled (library 'gtk+-2.0 >= 2.12' not found)
@end example
@end smallformat

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:
@smallformat
@example
  // Invoke just before entering Simulator::Run ()
  GtkConfigStore config;
  config.ConfigureDefaults ();
  config.ConfigureAttributes ();
@end example
@end smallformat

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.  

@subsection Future work
There are a couple of possible improvements:
@itemize @bullet
@item save a unique version number with date and time at start of file
@item save rng initial seed somewhere.
@item make each RandomVariable serialize its own initial seed and re-read
it later
@item add the default values
@end itemize