doc/manual/attributes.texi
author Tom Henderson <tomh@tomh.org>
Fri Aug 07 15:34:45 2009 -0700 (2009-08-07)
changeset 4699 ba5d6d52be7e
parent 4654 f292d41cb943
child 4700 3d08aef4a581
permissions -rw-r--r--
fix manual for ConfigStore
     1 @node Attributes
     2 @chapter Attributes 
     3 @anchor{chap:Attributes}
     4 
     5 @menu
     6 * Object Overview::::
     7 * Attribute Overview::
     8 * Extending attributes::
     9 * Adding new class type::
    10 * ConfigStore::
    11 @end menu
    12 
    13 
    14 In ns-3 simulations, there are two main aspects to configuration:
    15 @itemize @bullet
    16 @item the simulation topology and how objects are connected 
    17 @item the values used by the models instantiated in the topology
    18 @end itemize
    19 
    20 This chapter focuses on the second item above: how the many values
    21 in use in ns-3 are organized, documented, and modifiable by ns-3 users.
    22 The ns-3 attribute system is also the underpinning of how traces
    23 and statistics are gathered in the simulator. 
    24 
    25 Before delving into details of the attribute value system,
    26 it will help to review some basic properties of @code{class ns3::Object}.
    27 
    28 @node Object Overview
    29 @section Object Overview
    30 
    31 ns-3 is fundamentally a C++ object-based system.  By this we mean that
    32 new C++ classes (types) can be declared, defined, and subclassed
    33 as usual.
    34 
    35 Many ns-3 objects inherit from the @code{ns3::Object} base class.  These
    36 objects have some additional properties that we exploit for 
    37 organizing the system and improving the memory management
    38 of our objects:
    39 
    40 @itemize @bullet
    41 @item a "metadata" system that links the class name to a lot of 
    42 meta-information about the object, including the base class of the subclass,
    43 the set of accessible constructors in the subclass, and the set of 
    44 "attributes" of the subclass
    45 @item a reference counting smart pointer implementation, for memory
    46 management.
    47 @end itemize
    48 
    49 ns-3 objects that use the attribute system derive from either
    50 @code{ns3::Object} or @code{ns3::ObjectBase}.  Most ns-3 objects
    51 we will discuss derive from @code{ns3::Object}, but a few that
    52 are outside the smart pointer memory management framework derive
    53 from @code{ns3::ObjectBase}.
    54 
    55 Let's review a couple of properties of these objects.
    56 
    57 @subsection Smart pointers
    58 
    59 As introduced in the ns-3 tutorial, ns-3 objects are memory managed by a 
    60 @uref{http://en.wikipedia.org/wiki/Smart_pointer,,reference counting smart pointer implementation}, @code{class ns3::Ptr}. 
    61 
    62 Smart pointers are used extensively in the ns-3 APIs, to avoid passing
    63 references to heap-allocated objects that may cause memory leaks.  
    64 For most basic usage (syntax), treat a smart pointer like a regular pointer:
    65 @verbatim
    66   Ptr<WifiNetDevice> nd = ...;
    67   nd->CallSomeFunction ();
    68   // etc.
    69 @end verbatim
    70 
    71 @subsection CreateObject
    72 
    73 As we discussed above in @ref{Memory management and class Ptr},
    74 at the lowest-level API, objects of type @code{ns3::Object} are 
    75 not instantiated using @code{operator new} as usual but instead by
    76 a templated function called @code{CreateObject()}.
    77 
    78 A typical way to create such an object is as follows:
    79 @verbatim
    80   Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
    81 @end verbatim
    82 
    83 You can think of this as being functionally equivalent to:
    84 @verbatim
    85   WifiNetDevice* nd = new WifiNetDevice ();
    86 @end verbatim
    87 
    88 Objects that derive from @code{ns3::Object} must be allocated
    89 on the heap using CreateObject().  Those deriving from 
    90 @code{ns3::ObjectBase}, such as ns-3 helper functions and packet
    91 headers and trailers, can be allocated on the stack.  
    92 
    93 In some scripts, you may not see a lot of CreateObject() calls
    94 in the code;
    95 this is because there are some helper objects in effect that 
    96 are doing the CreateObject()s for you.
    97 
    98 @subsection TypeId
    99 
   100 ns-3 classes that derive from class ns3::Object can include
   101 a metadata class called @code{TypeId} that records meta-information
   102 about the class, for use in the object aggregation and component
   103 manager systems:
   104 @itemize @bullet
   105  @item a unique string identifying the class
   106  @item the base class of the subclass, within the metadata system
   107  @item the set of accessible constructors in the subclass
   108 @end itemize
   109 
   110 @subsection Object Summary
   111 
   112 Putting all of these concepts together, let's look at a specific
   113 example:  @code{class ns3::Node}.
   114 
   115 The public header file node.h has a declaration that includes
   116 a static GetTypeId function call:
   117 
   118 @verbatim
   119 class Node : public Object
   120 {
   121 public:
   122   static TypeId GetTypeId (void);
   123   ...
   124 @end verbatim
   125 
   126 This is defined in the node.cc file as follows:
   127 
   128 @verbatim
   129 TypeId 
   130 Node::GetTypeId (void)
   131 {
   132   static TypeId tid = TypeId ("ns3::Node")
   133     .SetParent<Object> ()
   134     .AddConstructor<Node> ()
   135     .AddAttribute ("DeviceList", "The list of devices associated to this Node.",
   136                    ObjectVectorValue (),
   137                    MakeObjectVectorAccessor (&Node::m_devices),
   138                    MakeObjectVectorChecker<NetDevice> ())
   139     .AddAttribute ("ApplicationList", "The list of applications associated to this Node.",
   140                    ObjectVectorValue (),
   141                    MakeObjectVectorAccessor (&Node::m_applications),
   142                    MakeObjectVectorChecker<Application> ())
   143     .AddAttribute ("Id", "The id (unique integer) of this Node.",
   144                    TypeId::ATTR_GET, // allow only getting it.
   145                    UintegerValue (0),
   146                    MakeUintegerAccessor (&Node::m_id),
   147                    MakeUintegerChecker<uint32_t> ())
   148     ;
   149   return tid;
   150 }
   151 @end verbatim
   152 
   153 Look at the TypeId of an ns-3 @code{Object} class as an extended form of run 
   154 time type information (RTTI).  The C++ language includes simple kind of RTTI
   155 in order to support @code{dynamic_cast} and @code{typeid} operators.
   156 
   157 The ``@code{.SetParent<Object> ()}'' call in the declaration above is used in 
   158 conjunction with our object aggregation mechanisms to allow safe up- and
   159 down-casing in inheritance trees during @code{GetObject}.
   160 
   161 The ``@code{.AddConstructor<Node> ()}'' call is used in conjunction with our
   162 abstract object factory mechanisms to allow us to construct C++ objects without
   163 forcing a user to know the concrete class of the object she is building.
   164 
   165 The three calls to ``@code{.AddAttribute}'' associate a given string with a 
   166 strongly typed value in the class.  Notice that you must provide a help string
   167 which may be displayed, for example, via command line processors.  Each 
   168 @code{Attribute} is associated with mechanisms for accessing the underlying
   169 member variable in the object (for example, @code{MakeUintegerAccessor} tells
   170 the generic @code{Attribute} code how to get to the node ID above).  There are
   171 also ``Checker'' methods which are used to validate values.
   172 
   173 When users want to create Nodes, they will usually call some form of 
   174 @code{CreateObject},
   175 
   176 @verbatim
   177   Ptr<Node> n = CreateObject<Node> ();
   178 @end verbatim
   179 
   180 or more abstractly, using an object factory, you can create a @code{Node} object
   181 without even knowing the concrete C++ type
   182 
   183 @verbatim
   184   ObjectFactory factory;
   185   const std::string typeId = "ns3::Node'';
   186   factory.SetTypeId(typeId);
   187   Ptr<Object> node = factory.Create <Object> ();
   188 @end verbatim
   189 
   190 Both of these methods result in fully initialized attributes being available 
   191 in the resulting @code{Object} instances.
   192 
   193 We next discuss how attributes (values associated with member variables
   194 or functions of the class) are plumbed into the above TypeId.
   195 
   196 @node Attribute Overview
   197 @section Attribute Overview
   198 
   199 The goal of the attribute system is to organize the access of
   200 internal member objects of a simulation.  This goal arises because,
   201 typically in simulation, users will cut and paste/modify existing
   202 simulation scripts, or will use higher-level simulation constructs,
   203 but often will be interested in studying or tracing particular 
   204 internal variables.  For instance, use cases such as:
   205 @itemize @bullet
   206 @item "I want to trace the packets on the wireless interface only on
   207 the first access point"
   208 @item "I want to trace the value of the TCP congestion window (every
   209 time it changes) on a particular TCP socket"
   210 @item "I want a dump of all values that were used in my simulation."
   211 @end itemize 
   212 
   213 Similarly, users may want fine-grained access to internal
   214 variables in the simulation, or may want to broadly change the
   215 initial value used for a particular parameter in all subsequently
   216 created objects.  Finally, users may wish to know what variables
   217 are settable and retrievable in a simulation configuration.  This
   218 is not just for direct simulation interaction on the command line; 
   219 consider also a (future) graphical user interface
   220 that would like to be able to provide a feature whereby a user
   221 might right-click on an node on the canvas and see a hierarchical,
   222 organized list of parameters that are settable on the node and its 
   223 constituent member objects, and help text and default values for
   224 each parameter.
   225 
   226 @subsection Functional overview
   227 
   228 We provide a way for users to access values deep in the system, without
   229 having to plumb accessors (pointers) through the system and walk 
   230 pointer chains to get to them.  Consider a class DropTailQueue that
   231 has a member variable that is an unsigned integer @code{m_maxPackets};
   232 this member variable controls the depth of the queue.  
   233 
   234 If we look at the declaration of DropTailQueue, we see the following:
   235 @verbatim
   236 class DropTailQueue : public Queue {
   237 public:
   238   static TypeId GetTypeId (void);
   239   ...
   240 
   241 private:
   242   std::queue<Ptr<Packet> > m_packets;
   243   uint32_t m_maxPackets;
   244 };
   245 @end verbatim
   246 
   247 Let's consider things that a user may want to do with the value of
   248 m_maxPackets:
   249 
   250 @itemize @bullet
   251 @item Set a default value for the system, such that whenever a new
   252 DropTailQueue is created, this member is initialized to that default. 
   253 @item Set or get the value on an already instantiated queue.
   254 @end itemize
   255 
   256 The above things typically require providing Set() and Get() functions,
   257 and some type of global default value.
   258 
   259 In the ns-3 attribute system, these value definitions and accessor
   260 functions are moved into the TypeId class; e.g.:  
   261 
   262 @verbatim
   263 NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
   264 
   265 TypeId DropTailQueue::GetTypeId (void) 
   266 {
   267   static TypeId tid = TypeId ("ns3::DropTailQueue")
   268     .SetParent<Queue> ()
   269     .AddConstructor<DropTailQueue> ()
   270     .AddAttribute ("MaxPackets", 
   271                    "The maximum number of packets accepted by this DropTailQueue.",
   272                    UintegerValue (100),
   273                    MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
   274                    MakeUintegerChecker<uint32_t> ())
   275     ;
   276   
   277   return tid;
   278 }
   279 @end verbatim
   280 
   281 The AddAttribute() method is performing a number of things with this
   282 value:
   283 @itemize @bullet
   284 @item Binding the variable m_maxPackets to a string "MaxPackets"
   285 @item Providing a default value (100 packets)
   286 @item Providing some help text defining the value
   287 @item Providing a "checker" (not used in this example) that can be used to set
   288 bounds on the allowable range of values
   289 @end itemize
   290 
   291 The key point is that now the value of this variable and its default
   292 value are accessible in the attribute namespace, which is based on
   293 strings such as "MaxPackets" and TypeId strings.  In the next
   294 section, we will provide an example script that shows how users
   295 may manipulate these values.
   296 
   297 Note that initialization of the attribute relies on the macro
   298 NS_OBJECT_ENSURE_REGISTERED (DropTailQueue) being called; if you leave
   299 this out of your new class implementation, your attributes will not be 
   300 initialized corretly.
   301 
   302 @subsection Basic usage
   303 
   304 Let's look at how a user script might access these values.  
   305 This is based on the script found at @code{samples/main-attribute-value.cc},
   306 with some details stripped out.
   307 @verbatim
   308 //
   309 // This is a basic example of how to use the attribute system to
   310 // set and get a value in the underlying system; namely, an unsigned
   311 // integer of the maximum number of packets in a queue
   312 //
   313 
   314 int 
   315 main (int argc, char *argv[])
   316 {
   317 
   318   // By default, the MaxPackets attribute has a value of 100 packets
   319   // (this default can be observed in the function DropTailQueue::GetTypeId)
   320   // 
   321   // Here, we set it to 80 packets.  We could use one of two value types:
   322   // a string-based value or a Uinteger value
   323   Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
   324   // The below function call is redundant
   325   Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
   326 
   327   // Allow the user to override any of the defaults and the above
   328   // SetDefaults() at run-time, via command-line arguments
   329   CommandLine cmd;
   330   cmd.Parse (argc, argv);
   331 @end verbatim
   332 
   333 The main thing to notice in the above are the two calls to 
   334 @code{Config::SetDefault}.  This is how we set the default value
   335 for all subsequently instantiated DropTailQueues.  We illustrate
   336 that two types of Value classes, a StringValue and a UintegerValue class,
   337 can be used to assign the value to the attribute named by
   338 "ns3::DropTailQueue::MaxPackets".
   339 
   340 Now, we will create a few objects using the low-level API; here,
   341 our newly created queues will not have a m_maxPackets initialized to
   342 100 packets but to 80 packets, because of what we did above with
   343 default values.
   344 @verbatim
   345   Ptr<Node> n0 = CreateObject<Node> ();
   346 
   347   Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();
   348   n0->AddDevice (net0);
   349 
   350   Ptr<Queue> q = CreateObject<DropTailQueue> ();
   351   net0->AddQueue(q);
   352 @end verbatim
   353 
   354 At this point, we have created a single node (Node 0) and a 
   355 single PointToPointNetDevice (NetDevice 0) and added a 
   356 DropTailQueue to it.
   357 
   358 Now, we can manipulate the MaxPackets value of the already 
   359 instantiated DropTailQueue.  Here are various ways to do that.
   360 
   361 @subsubsection Pointer-based access
   362 
   363 We assume that a smart pointer (Ptr) to a relevant network device is 
   364 in hand; here, it is the net0 pointer. 
   365 
   366 One way to change the value is to access a pointer to the
   367 underlying queue and modify its attribute.
   368  
   369 First, we observe that we can get a pointer to the (base class)
   370 queue via the PointToPointNetDevice attributes, where it is called
   371 TxQueue 
   372 @verbatim
   373   PointerValue tmp;
   374   net0->GetAttribute ("TxQueue", tmp);
   375   Ptr<Object> txQueue = tmp.GetObject ();
   376 @end verbatim
   377 
   378 Using the GetObject function, we can perform a safe downcast
   379 to a DropTailQueue, where MaxPackets is a member
   380 @verbatim
   381   Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
   382   NS_ASSERT (dtq != 0);
   383 @end verbatim
   384 
   385 Next, we can get the value of an attribute on this queue.
   386 We have introduced wrapper "Value" classes for the underlying
   387 data types, similar to Java wrappers around these types, since
   388 the attribute system stores values and not disparate types.
   389 Here, the attribute value is assigned to a UintegerValue, and
   390 the Get() method on this value produces the (unwrapped) uint32_t.
   391 @verbatim
   392   UintegerValue limit;
   393   dtq->GetAttribute ("MaxPackets", limit);
   394   NS_LOG_INFO ("1.  dtq limit: " << limit.Get () << " packets");
   395 @end verbatim
   396   
   397 Note that the above downcast is not really needed; we could have
   398 done the same using the Ptr<Queue> even though the attribute
   399 is a member of the subclass
   400 @verbatim
   401   txQueue->GetAttribute ("MaxPackets", limit);
   402   NS_LOG_INFO ("2.  txQueue limit: " << limit.Get () << " packets");
   403 @end verbatim
   404 
   405 Now, let's set it to another value (60 packets)
   406 @verbatim
   407   txQueue->SetAttribute("MaxPackets", UintegerValue (60));
   408   txQueue->GetAttribute ("MaxPackets", limit);
   409   NS_LOG_INFO ("3.  txQueue limit changed: " << limit.Get () << " packets");
   410 @end verbatim
   411 
   412 @subsubsection Namespace-based access
   413 
   414 An alternative way to get at the attribute is to use the configuration namespace.
   415 Here, this attribute resides on a known path in this namespace; this approach
   416 is useful if one doesn't have access to the underlying pointers and would like
   417 to configure a specific attribute with a single statement.  
   418 
   419 @verbatim
   420   Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue (25));
   421   txQueue->GetAttribute ("MaxPackets", limit); 
   422   NS_LOG_INFO ("4.  txQueue limit changed through namespace: " << 
   423     limit.Get () << " packets");
   424 @end verbatim
   425 
   426 We could have also used wildcards to set this value for all nodes and all net 
   427 devices (which in this simple example has the same effect as the previous Set())
   428 @verbatim
   429   Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue (15));
   430   txQueue->GetAttribute ("MaxPackets", limit); 
   431   NS_LOG_INFO ("5.  txQueue limit changed through wildcarded namespace: " << 
   432     limit.Get () << " packets");
   433 @end verbatim
   434 
   435 @subsubsection Object Name Service-based access
   436 
   437 Another way to get at the attribute is to use the object name service facility.
   438 Here, this attribute is found using a name string.  This approach is useful if 
   439 one doesn't have access to the underlying pointers and it is difficult to 
   440 determine the required concrete configuration namespaced path.
   441 
   442 @verbatim
   443   Names::Add ("server", serverNode);
   444   Names::Add ("server/eth0", serverDevice);
   445 
   446   ...
   447 
   448   Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
   449 @end verbatim
   450 
   451 @subsection Setting through constructors helper classes
   452 
   453 Arbitrary combinations of attributes can be set and fetched from
   454 the helper and low-level APIs; either from the constructors themselves:
   455 @verbatim
   456 Ptr<Object> p = CreateObject<MyNewObject> ("n1", v1, "n2", v2, ...);
   457 @end verbatim
   458 or from the higher-level helper APIs, such as:
   459 @verbatim
   460   mobility.SetPositionAllocator ("GridPositionAllocator",
   461                                  "MinX", DoubleValue (-100.0),
   462                                  "MinY", DoubleValue (-100.0),
   463                                  "DeltaX", DoubleValue (5.0),
   464                                  "DeltaY", DoubleValue (20.0),
   465                                  "GridWidth", UintegerValue (20),
   466                                  "LayoutType", StringValue ("RowFirst"));
   467 @end verbatim
   468 
   469 @subsection Value classes
   470 Readers will note the new FooValue classes which are subclasses of the
   471 AttributeValue base class.  These can be thought of as
   472 an intermediate class that can be used to convert from raw types to the
   473 Values that are used by the attribute system.  Recall that this database is holding
   474 objects of many types with a single generic type.  Conversions to this
   475 type can either be done using an intermediate class (IntegerValue, DoubleValue for
   476 "floating point") or via strings.  Direct implicit conversion of types
   477 to Value is not really practical.  So in the above, users have a choice
   478 of using strings or values: 
   479 @verbatim
   480 p->Set ("cwnd", StringValue ("100")); // string-based setter
   481 p->Set ("cwnd", IntegerValue (100)); // integer-based setter
   482 @end verbatim
   483 
   484 The system provides some macros that help users declare and define
   485 new AttributeValue subclasses for new types that they want to introduce into
   486 the attribute system: 
   487 @itemize @bullet
   488 @item ATTRIBUTE_HELPER_HEADER
   489 @item ATTRIBUTE_HELPER_CPP
   490 @end itemize
   491 
   492 @node Extending attributes
   493 @section Extending attributes
   494 
   495 The ns-3 system will place a number of internal values under the
   496 attribute system, but undoubtedly users will want to extend this
   497 to pick up ones we have missed, or to add their own classes to this.
   498 
   499 @subsection Adding an existing internal variable to the metadata system 
   500 
   501 Consider this variable in class TcpSocket:
   502 @verbatim
   503  uint32_t m_cWnd;   // Congestion window
   504 @end verbatim
   505 
   506 Suppose that someone working with Tcp wanted to get or set the 
   507 value of that variable using the metadata system.  If it were not
   508 already provided by ns-3, the user could declare the following addition 
   509 in the runtime metadata system (to the TypeId declaration for TcpSocket):
   510 @verbatim
   511     .AddAttribute ("Congestion window", 
   512                    "Tcp congestion window (bytes)",
   513                    UintegerValue (1),
   514                    MakeUintegerAccessor (&TcpSocket::m_cWnd),
   515                    MakeUintegerChecker<uint16_t> ())
   516 
   517 @end verbatim
   518 
   519 Now, the user with a pointer to the TcpSocket can perform operations
   520 such as setting and getting the value, without having to add these
   521 functions explicitly.  Furthermore, access controls can be applied, such
   522 as allowing the parameter to be read and not written, or bounds
   523 checking on the permissible values can be applied.
   524 
   525 @subsection Adding a new TypeId
   526 
   527 Here, we discuss the impact on a user who wants to add a new class to
   528 ns-3; what additional things must be done to hook it into this system.
   529 
   530 We've already introduced what a TypeId definition looks like:
   531 @verbatim
   532 TypeId
   533 RandomWalk2dMobilityModel::GetTypeId (void)
   534 {
   535   static TypeId tid = TypeId ("ns3::RandomWalk2dMobilityModel")
   536     .SetParent<MobilityModel> ()
   537     .SetGroupName ("Mobility")
   538     .AddConstructor<RandomWalk2dMobilityModel> ()
   539     .AddAttribute ("Bounds",
   540                    "Bounds of the area to cruise.",
   541                    RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
   542                    MakeRectangleAccessor (&RandomWalk2dMobilityModel::m_bounds),
   543                    MakeRectangleChecker ())
   544     .AddAttribute ("Time",
   545                    "Change current direction and speed after moving for this delay.",
   546                    TimeValue (Seconds (1.0)),
   547                    MakeTimeAccessor (&RandomWalk2dMobilityModel::m_modeTime),
   548                    MakeTimeChecker ())
   549     // etc (more parameters).
   550     ;
   551   return tid;
   552 }
   553 @end verbatim
   554 
   555 The declaration for this in the class declaration is one-line public
   556 member method:
   557 @verbatim
   558  public:
   559   static TypeId GetTypeId (void);
   560 @end verbatim
   561 
   562 Typical mistakes here involve:
   563 @itemize @bullet
   564 @item Not calling the SetParent method or calling it with the wrong type
   565 @item Not calling the AddConstructor method of calling it with the wrong type
   566 @item Introducing a typographical error in the name of the TypeId in its constructor
   567 @item Not using the fully-qualified c++ typename of the enclosing c++ class as the 
   568 name of the TypeId
   569 @end itemize
   570 None of these mistakes can be detected by the ns-3 codebase so, users
   571 are advised to check carefully multiple times that they got these right.
   572 
   573 
   574 @node Adding new class type
   575 @section Adding new class type to the attribute system
   576 
   577 From the perspective of the user who writes a new class in the system and
   578 wants to hook it in to the attribute system, there is mainly the matter 
   579 of writing 
   580 the conversions to/from strings and attribute values.  Most of this can be
   581 copy/pasted with macro-ized code.  For instance, consider class
   582 delcaration for Rectangle in the @code{src/mobility/} directory:
   583 
   584 @verbatim
   585 /**
   586  * \brief a 2d rectangle
   587  */
   588 class Rectangle
   589 {
   590   ...
   591 
   592   double xMin;
   593   double xMax;
   594   double yMin;
   595   double yMax;
   596 };
   597 @end verbatim
   598  
   599 One macro call and two operators, must be added below the class declaration
   600 in order to turn a Rectangle into a value usable by the @code{Attribute}
   601 system:
   602 
   603 @verbatim
   604 std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
   605 std::istream &operator >> (std::istream &is, Rectangle &rectangle);
   606 
   607 ATTRIBUTE_HELPER_HEADER (Rectangle);
   608 @end verbatim
   609 
   610 In the class definition (@code{.cc} file), the code looks like this:
   611 
   612 @verbatim
   613 ATTRIBUTE_HELPER_CPP (Rectangle);
   614 
   615 std::ostream &
   616 operator << (std::ostream &os, const Rectangle &rectangle)
   617 {
   618   os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|" << rectangle.yMax;
   619   return os;
   620 }
   621 std::istream &
   622 operator >> (std::istream &is, Rectangle &rectangle)
   623  {
   624   char c1, c2, c3;
   625   is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3 >> rectangle.yMax;
   626   if (c1 != '|' ||
   627       c2 != '|' ||
   628       c3 != '|')
   629     {
   630       is.setstate (std::ios_base::failbit);
   631     }
   632   return is;
   633 }
   634 @end verbatim
   635 
   636 These stream operators simply convert from a string representation of the
   637 Rectangle ("xMin|xMax|yMin|yMax") to the underlying Rectangle, and the
   638 modeler must specify these operators and the string syntactical representation 
   639 of an instance of the new class.
   640 
   641 @node ConfigStore
   642 @section ConfigStore
   643 
   644 @strong{Feedback requested:}  This is an experimental feature of ns-3.  It is 
   645 found in @code{src/contrib} and not in the main tree.  If you like this feature
   646 and would like to provide feedback on it, please email us.
   647 
   648 Values for ns-3 attributes can be stored in an ascii or XML text file and
   649 loaded into a future simulation.  This feature is known as the
   650 ns-3 ConfigStore.  
   651 The ConfigStore code is in @code{src/contrib/}.  It is not yet main-tree
   652 code, because we are seeking some user feedback and experience with this. 
   653 
   654 We can explore this system by using an example.  Copy the @code{csma-bridge.cc}
   655 file to the scratch directory:
   656 @verbatim
   657   cp examples/csma-bridge.cc scratch/
   658   ./waf
   659 @end verbatim
   660 
   661 Let's edit it to add the ConfigStore feature.  First, add an include statement 
   662 to include the contrib module, and then add these lines:
   663 
   664 @verbatim
   665 #include "contrib-module.h"
   666 ...
   667 int main (...)
   668 {
   669   // setup topology
   670 
   671   // Invoke just before entering Simulator::Run ()
   672   ConfigStore config;
   673   config.ConfigureDefaults ();
   674   config.ConfigureAttributes ();
   675 
   676   Simulator::Run ();
   677 }
   678 @end verbatim
   679 
   680 There are three attributes that govern the behavior of the ConfigStore:
   681 "Mode", "Filename", and "FileFormat".  The Mode (default "None") configures
   682 whether ns-3 should load configuration from a previously saved file
   683 (specify "Mode=Load") or save it to a file (specify "Mode=Save").
   684 The Filename (default "") is where the ConfigStore should store its
   685 output data.  The FileFormat (default "RawText") governs whether
   686 the ConfigStore format is Xml or RawText format.
   687 
   688 So, using the above modified program, try executing the following
   689 waf command and 
   690 @verbatim
   691 ./waf --command-template="%s --ns3::ConfigStore::Filename=csma-bridge-config.xml --ns3::ConfigStore::Mode=Save --ns3::ConfigStore::FileFormat=Xml" --run scratch/csma-bridge
   692 @end verbatim
   693 After running, you can open the csma-bridge-config.xml file and it will
   694 display the configuration that was applied to your simulation; e.g.
   695 @verbatim
   696 <?xml version="1.0" encoding="UTF-8"?>
   697 <ns3>
   698  <default name="ns3::V4Ping::Remote" value="102.102.102.102"/>
   699  <default name="ns3::MsduStandardAggregator::MaxAmsduSize" value="7935"/>
   700  <default name="ns3::EdcaTxopN::MinCw" value="31"/>
   701  <default name="ns3::EdcaTxopN::MaxCw" value="1023"/>
   702  <default name="ns3::EdcaTxopN::Aifsn" value="3"/>
   703  <default name="ns3::QstaWifiMac::ProbeRequestTimeout" value="50000000ns"/>
   704  <default name="ns3::QstaWifiMac::AssocRequestTimeout" value="500000000ns"/>
   705  <default name="ns3::QstaWifiMac::MaxMissedBeacons" value="10"/>
   706  <default name="ns3::QstaWifiMac::ActiveProbing" value="false"/>
   707 ...
   708 @end verbatim
   709 This file can be archived with your simulation script and output data.
   710 
   711 While it is possible to generate a sample config file and lightly
   712 edit it to change a couple of values, there are cases where this
   713 process will not work because the same value on the same object
   714 can appear multiple times in the same automatically-generated 
   715 configuration file under different configuration paths.
   716 
   717 As such, the best way to use this class is to use it to generate
   718 an initial configuration file, extract from that configuration
   719 file only the strictly necessary elements, and move these minimal
   720 elements to a new configuration file which can then safely
   721 be edited and loaded in a subsequent simulation run. 
   722 
   723 When the ConfigStore object is instantiated, its attributes Filename,
   724 Mode, and FileFormat must be set, either via command-line or via
   725 program statements.  
   726 
   727 As a more complicated example, let's assume that we want to 
   728 read in a configuration of defaults from an input file named
   729 "input-defaults.xml", and write out the resulting attributes to a
   730 separate file called "output-attributes.xml".  (Note-- to get this
   731 input xml file to begin with, it is sometimes helpful to run the
   732 program to generate an output xml file first, then hand-edit that
   733 file and re-input it for the next simulation run).
   734 @verbatim
   735 #include "contrib-module.h"
   736 ...
   737 int main (...)
   738 {
   739 
   740   Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
   741   Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
   742   Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
   743   ConfigStore inputConfig;
   744   inputConfig.ConfigureDefaults ();
   745   
   746   //
   747   // Allow the user to override any of the defaults and the above Bind() at
   748   // run-time, via command-line arguments
   749   //
   750   CommandLine cmd;
   751   cmd.Parse (argc, argv);
   752 
   753   // setup topology
   754   ...
   755 
   756   // Invoke just before entering Simulator::Run ()
   757   Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
   758   Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
   759   ConfigStore outputConfig;
   760   outputConfig.ConfigureAttributes ();
   761   Simulator::Run ();
   762 }
   763 @end verbatim
   764 
   765 @subsection GTK-based ConfigStore
   766 
   767 There is a GTK-based front end for the ConfigStore.  This allows users
   768 to use a GUI to access and change variables.  Screenshots of this
   769 feature are available in the 
   770 @uref{http://www.nsnam.org/docs/ns-3-overview.pdf,,ns-3 Overview} presentation.
   771 
   772 To use this feature, one must install libgtk and libgtk-dev; an example
   773 Ubuntu installation command is:
   774 @verbatim
   775 sudo apt-get install libgtk2.0-0 libgtk2.0-dev
   776 @end verbatim
   777 To check whether it is configured or not, check the output of the
   778 ./waf configure step:
   779 @verbatim
   780 ---- Summary of optional NS-3 features:
   781 Threading Primitives          : enabled
   782 Real Time Simulator           : enabled
   783 GtkConfigStore                : not enabled (library 'gtk+-2.0 >= 2.12' not found)
   784 @end verbatim
   785 
   786 In the above example, it was not enabled, so it cannot be used until a
   787 suitable version is installed and ./waf configure; ./waf is rerun.
   788 
   789 Usage is almost the same as the non-GTK-based version, but there
   790 are no ConfigStore attributes involved:
   791 @verbatim
   792   // Invoke just before entering Simulator::Run ()
   793   GtkConfigStore config;
   794   config.ConfigureDefaults ();
   795   config.ConfigureAttributes ();
   796 @end verbatim
   797 
   798 Now, when you run the script, a GUI should pop up, allowing you to open
   799 menus of attributes on different nodes/objects, and then launch the
   800 simulation execution when you are done.  
   801 
   802 @subsection Future work
   803 There are a couple of possible improvements:
   804 @itemize @bullet
   805 @item save a unique version number with date and time at start of file
   806 @item save rng initial seed somewhere.
   807 @item make each RandomVariable serialize its own initial seed and re-read
   808 it later
   809 @item add the default values
   810 @end itemize
   811