A Discrete-Event Network Simulator
API
names.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 University of Washington
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17 
18 #include "names.h"
19 
20 #include "abort.h"
21 #include "assert.h"
22 #include "log.h"
23 #include "object.h"
24 #include "singleton.h"
25 
26 #include <map>
27 
34 namespace ns3
35 {
36 
38 
43 class NameNode
44 {
45  public:
47  NameNode();
53  NameNode(const NameNode& nameNode);
61  NameNode(NameNode* parent, std::string name, Ptr<Object> object);
68  NameNode& operator=(const NameNode& rhs);
69 
71  ~NameNode();
72 
76  std::string m_name;
79 
81  std::map<std::string, NameNode*> m_nameMap;
82 };
83 
85  : m_parent(nullptr),
86  m_name(""),
87  m_object(nullptr)
88 {
89 }
90 
91 NameNode::NameNode(const NameNode& nameNode)
92 {
93  m_parent = nameNode.m_parent;
94  m_name = nameNode.m_name;
95  m_object = nameNode.m_object;
96  m_nameMap = nameNode.m_nameMap;
97 }
98 
99 NameNode&
101 {
102  m_parent = rhs.m_parent;
103  m_name = rhs.m_name;
104  m_object = rhs.m_object;
105  m_nameMap = rhs.m_nameMap;
106  return *this;
107 }
108 
109 NameNode::NameNode(NameNode* parent, std::string name, Ptr<Object> object)
110  : m_parent(parent),
111  m_name(name),
112  m_object(object)
113 {
114  NS_LOG_FUNCTION(this << parent << name << object);
115 }
116 
118 {
119  NS_LOG_FUNCTION(this);
120 }
121 
126 class NamesPriv : public Singleton<NamesPriv>
127 {
128  public:
130  NamesPriv();
132  ~NamesPriv() override;
133 
134  // Doxygen \copydoc bug: won't copy these docs, so we repeat them.
135 
144  bool Add(std::string name, Ptr<Object> object);
154  bool Add(std::string path, std::string name, Ptr<Object> object);
165  bool Add(Ptr<Object> context, std::string name, Ptr<Object> object);
166 
175  bool Rename(std::string oldpath, std::string newname);
187  bool Rename(std::string path, std::string oldname, std::string newname);
201  bool Rename(Ptr<Object> context, std::string oldname, std::string newname);
202 
211  std::string FindName(Ptr<Object> object);
220  std::string FindPath(Ptr<Object> object);
221 
225  void Clear();
226 
235  Ptr<Object> Find(std::string path);
245  Ptr<Object> Find(std::string path, std::string name);
256  Ptr<Object> Find(Ptr<Object> context, std::string name);
257 
258  private:
265  NameNode* IsNamed(Ptr<Object> object);
273  bool IsDuplicateName(NameNode* node, std::string name);
274 
277 
279  std::map<Ptr<Object>, NameNode*> m_objectMap;
280 };
281 
283 {
284  NS_LOG_FUNCTION(this);
285 
286  m_root.m_parent = nullptr;
287  m_root.m_name = "Names";
288  m_root.m_object = nullptr;
289 }
290 
292 {
293  NS_LOG_FUNCTION(this);
294  Clear();
295  m_root.m_name = "";
296 }
297 
298 void
300 {
301  NS_LOG_FUNCTION(this);
302  //
303  // Every name is associated with an object in the object map, so freeing the
304  // NameNodes in this map will free all of the memory allocated for the NameNodes
305  //
306  for (auto i = m_objectMap.begin(); i != m_objectMap.end(); ++i)
307  {
308  delete i->second;
309  i->second = nullptr;
310  }
311 
312  m_objectMap.clear();
313 
314  m_root.m_parent = nullptr;
315  m_root.m_name = "Names";
316  m_root.m_object = nullptr;
317  m_root.m_nameMap.clear();
318 }
319 
320 bool
321 NamesPriv::Add(std::string name, Ptr<Object> object)
322 {
323  NS_LOG_FUNCTION(this << name << object);
324  //
325  // This is the simple, easy to use version of Add, so we want it to be flexible.
326  // We don't want to force a user to always type the fully qualified namespace
327  // name, so we allow the namespace name to be omitted. For example, calling
328  // Add ("Client/ath0", obj) should result in exactly the same behavior as
329  // Add ("/Names/Client/ath0", obj). Calling Add ("Client", obj) should have
330  // the same effect as Add ("Names/Client", obj)
331  //
332  // The first thing to do, then, is to "canonicalize" the input string to always
333  // be a fully qualified name.
334  //
335  // If we are given a name that begins with "/Names/" we assume that this is a
336  // fully qualified path name to the object we want to create. We split the name
337  // into a path string and a final segment (name) and then call the "Real" Add.
338  //
339  std::string namespaceName = "/Names";
340  std::string::size_type offset = name.find(namespaceName);
341  if (offset != 0)
342  {
343  //
344  // This must be a name that has the "/Names" namespace prefix omitted.
345  // Do some reasonableness checking on the rest of the name.
346  //
347  offset = name.find('/');
348  if (offset == 0)
349  {
350  NS_ASSERT_MSG(false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
351  return false;
352  }
353 
354  name = "/Names/" + name;
355  }
356 
357  //
358  // There must now be a fully qualified path in the string. All fully
359  // qualified names begin with "/Names". We have to split off the final
360  // segment which will become the name of the object. A '/' that
361  // separates the path from the final segment had better be there since
362  // we just made sure that at least the namespace name was there.
363  //
364  std::string::size_type i = name.rfind('/');
365  NS_ASSERT_MSG(i != std::string::npos,
366  "NamesPriv::Add(): Internal error. Can't find '/' in name");
367 
368  //
369  // The slash we found cannot be the slash at the start of the namespaceName.
370  // This would indicate there is no name in the path at all. It can be
371  // any other index.
372  //
373  NS_ASSERT_MSG(i != 0, "NamesPriv::Add(): Can't find a name in the path string");
374 
375  //
376  // We now know where the path string starts and ends, and where the
377  // name starts and ends. All we have to do is to call our available
378  // function for adding a name under a path string.
379  //
380  return Add(name.substr(0, i), name.substr(i + 1), object);
381 }
382 
383 bool
384 NamesPriv::Add(std::string path, std::string name, Ptr<Object> object)
385 {
386  NS_LOG_FUNCTION(this << path << name << object);
387  if (path == "/Names")
388  {
389  return Add(Ptr<Object>(nullptr, false), name, object);
390  }
391  return Add(Find(path), name, object);
392 }
393 
394 bool
395 NamesPriv::Add(Ptr<Object> context, std::string name, Ptr<Object> object)
396 {
397  NS_LOG_FUNCTION(this << context << name << object);
398 
399  if (IsNamed(object))
400  {
401  NS_LOG_LOGIC("Object is already named");
402  return false;
403  }
404 
405  NameNode* node = nullptr;
406  if (context)
407  {
408  node = IsNamed(context);
409  NS_ASSERT_MSG(node, "NamesPriv::Name(): context must point to a previously named node");
410  }
411  else
412  {
413  node = &m_root;
414  }
415 
416  if (IsDuplicateName(node, name))
417  {
418  NS_LOG_LOGIC("Name is already taken");
419  return false;
420  }
421 
422  auto newNode = new NameNode(node, name, object);
423  node->m_nameMap[name] = newNode;
424  m_objectMap[object] = newNode;
425 
426  return true;
427 }
428 
429 bool
430 NamesPriv::Rename(std::string oldpath, std::string newname)
431 {
432  NS_LOG_FUNCTION(this << oldpath << newname);
433  //
434  // This is the simple, easy to use version of Rename, so we want it to be
435  // flexible. We don't want to force a user to always type the fully
436  // qualified namespace name, so we allow the namespace name to be omitted.
437  // For example, calling Rename ("Client/ath0", "eth0") should result in
438  // exactly the same behavior as Rename ("/Names/Client/ath0", "eth0").
439  // Calling Rename ("Client", "Router") should have the same effect as
440  // Rename ("Names/Client", "Router")
441  //
442  // The first thing to do, then, is to "canonicalize" the input string to always
443  // be a fully qualified path.
444  //
445  // If we are given a name that begins with "/Names/" we assume that this is a
446  // fully qualified path to the object we want to change. We split the path into
447  // path string (cf directory) and a final segment (cf filename) and then call
448  // the "Real" Rename.
449  //
450  std::string namespaceName = "/Names";
451  std::string::size_type offset = oldpath.find(namespaceName);
452  if (offset != 0)
453  {
454  //
455  // This must be a name that has the "/Names" namespace prefix omitted.
456  // Do some reasonableness checking on the rest of the name.
457  //
458  offset = oldpath.find('/');
459  if (offset == 0)
460  {
461  NS_ASSERT_MSG(false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
462  return false;
463  }
464 
465  oldpath = "/Names/" + oldpath;
466  }
467 
468  //
469  // There must now be a fully qualified path in the oldpath string. All
470  // fully qualified names begin with "/Names". We have to split off the final
471  // segment which will become the name we want to rename. A '/' that
472  // separates the path from the final segment (name) had better be there since
473  // we just made sure that at least the namespace name was there.
474  //
475  std::string::size_type i = oldpath.rfind('/');
476  NS_ASSERT_MSG(i != std::string::npos,
477  "NamesPriv::Add(): Internal error. Can't find '/' in name");
478 
479  //
480  // The slash we found cannot be the slash at the start of the namespaceName.
481  // This would indicate there is no name in the path at all. It can be
482  // any other index.
483  //
484  NS_ASSERT_MSG(i != 0, "NamesPriv::Add(): Can't find a name in the path string");
485 
486  //
487  // We now know where the path part of the string starts and ends, and where the
488  // name part starts and ends. All we have to do is to call our available
489  // function for creating adding a name under a path string.
490  //
491  return Rename(oldpath.substr(0, i), oldpath.substr(i + 1), newname);
492 }
493 
494 bool
495 NamesPriv::Rename(std::string path, std::string oldname, std::string newname)
496 {
497  NS_LOG_FUNCTION(this << path << oldname << newname);
498  if (path == "/Names")
499  {
500  return Rename(Ptr<Object>(nullptr, false), oldname, newname);
501  }
502  return Rename(Find(path), oldname, newname);
503 }
504 
505 bool
506 NamesPriv::Rename(Ptr<Object> context, std::string oldname, std::string newname)
507 {
508  NS_LOG_FUNCTION(this << context << oldname << newname);
509 
510  NameNode* node = nullptr;
511  if (context)
512  {
513  node = IsNamed(context);
514  NS_ASSERT_MSG(node, "NamesPriv::Name(): context must point to a previously named node");
515  }
516  else
517  {
518  node = &m_root;
519  }
520 
521  if (IsDuplicateName(node, newname))
522  {
523  NS_LOG_LOGIC("New name is already taken");
524  return false;
525  }
526 
527  auto i = node->m_nameMap.find(oldname);
528  if (i == node->m_nameMap.end())
529  {
530  NS_LOG_LOGIC("Old name does not exist in name map");
531  return false;
532  }
533  else
534  {
535  NS_LOG_LOGIC("Old name exists in name map");
536 
537  //
538  // The rename process consists of:
539  // 1. Getting the pointer to the name node from the map and remembering it;
540  // 2. Removing the map entry corresponding to oldname from the map;
541  // 3. Changing the name string in the name node;
542  // 4. Adding the name node back in the map under the newname.
543  //
544  NameNode* changeNode = i->second;
545  node->m_nameMap.erase(i);
546  changeNode->m_name = newname;
547  node->m_nameMap[newname] = changeNode;
548  return true;
549  }
550 }
551 
552 std::string
554 {
555  NS_LOG_FUNCTION(this << object);
556 
557  auto i = m_objectMap.find(object);
558  if (i == m_objectMap.end())
559  {
560  NS_LOG_LOGIC("Object does not exist in object map");
561  return "";
562  }
563  else
564  {
565  NS_LOG_LOGIC("Object exists in object map");
566  return i->second->m_name;
567  }
568 }
569 
570 std::string
572 {
573  NS_LOG_FUNCTION(this << object);
574 
575  auto i = m_objectMap.find(object);
576  if (i == m_objectMap.end())
577  {
578  NS_LOG_LOGIC("Object does not exist in object map");
579  return "";
580  }
581 
582  NameNode* p = i->second;
583  NS_ASSERT_MSG(p,
584  "NamesPriv::FindFullName(): Internal error: Invalid NameNode pointer from map");
585 
586  std::string path;
587 
588  do
589  {
590  path = "/" + p->m_name + path;
591  NS_LOG_LOGIC("path is " << path);
592  } while ((p = p->m_parent) != nullptr);
593 
594  return path;
595 }
596 
598 NamesPriv::Find(std::string path)
599 {
600  //
601  // This is hooked in from simple, easy to use version of Find, so we want it
602  // to be flexible.
603  //
604  // If we are provided a path that doesn't begin with "/Names", we assume
605  // that the caller has simply given us a path starting with a name that
606  // is in the root namespace. This allows people to omit the "/Names" prefix.
607  // and simply do a Find ("Client/eth0") instead of having to always do a
608  // Find ("/Names/Client/eth0");
609  //
610  // So, if we are given a name that begins with "/Names/" the upshot is that we
611  // just remove that prefix and treat the rest of the string as starting with a
612  // name in the root namespace.
613  //
614 
615  NS_LOG_FUNCTION(this << path);
616  std::string namespaceName = "/Names/";
617  std::string remaining;
618 
619  std::string::size_type offset = path.find(namespaceName);
620  if (offset == 0)
621  {
622  NS_LOG_LOGIC(path << " is a fully qualified name");
623  remaining = path.substr(namespaceName.size());
624  }
625  else
626  {
627  NS_LOG_LOGIC(path << " begins with a relative name");
628  remaining = path;
629  }
630 
631  NameNode* node = &m_root;
632 
633  //
634  // The string <remaining> is now composed entirely of path segments in
635  // the /Names name space and we have eaten the leading slash. e.g.,
636  // remaining = "ClientNode/eth0"
637  //
638  // The start of the search is always at the root of the name space.
639  //
640  for (;;)
641  {
642  NS_LOG_LOGIC("Looking for the object of name " << remaining);
643  offset = remaining.find('/');
644  if (offset == std::string::npos)
645  {
646  //
647  // There are no remaining slashes so this is the last segment of the
648  // specified name. We're done when we find it
649  //
650  auto i = node->m_nameMap.find(remaining);
651  if (i == node->m_nameMap.end())
652  {
653  NS_LOG_LOGIC("Name does not exist in name map");
654  return nullptr;
655  }
656  else
657  {
658  NS_LOG_LOGIC("Name parsed, found object");
659  return i->second->m_object;
660  }
661  }
662  else
663  {
664  //
665  // There are more slashes so this is an intermediate segment of the
666  // specified name. We need to "recurse" when we find this segment.
667  //
668  offset = remaining.find('/');
669  std::string segment = remaining.substr(0, offset);
670 
671  auto i = node->m_nameMap.find(segment);
672  if (i == node->m_nameMap.end())
673  {
674  NS_LOG_LOGIC("Name does not exist in name map");
675  return nullptr;
676  }
677  else
678  {
679  node = i->second;
680  remaining = remaining.substr(offset + 1);
681  NS_LOG_LOGIC("Intermediate segment parsed");
682  continue;
683  }
684  }
685  }
686 
687  NS_ASSERT_MSG(node, "NamesPriv::Find(): Internal error: this can't happen");
688  return nullptr;
689 }
690 
692 NamesPriv::Find(std::string path, std::string name)
693 {
694  NS_LOG_FUNCTION(this << path << name);
695 
696  if (path == "/Names")
697  {
698  return Find(Ptr<Object>(nullptr, false), name);
699  }
700  return Find(Find(path), name);
701 }
702 
704 NamesPriv::Find(Ptr<Object> context, std::string name)
705 {
706  NS_LOG_FUNCTION(this << context << name);
707 
708  NameNode* node = nullptr;
709 
710  if (!context)
711  {
712  NS_LOG_LOGIC("Zero context implies root NameNode");
713  node = &m_root;
714  }
715  else
716  {
717  node = IsNamed(context);
718  if (node == nullptr)
719  {
720  NS_LOG_LOGIC("Context does not point to a previously named node");
721  return nullptr;
722  }
723  }
724 
725  auto i = node->m_nameMap.find(name);
726  if (i == node->m_nameMap.end())
727  {
728  NS_LOG_LOGIC("Name does not exist in name map");
729  return nullptr;
730  }
731  else
732  {
733  NS_LOG_LOGIC("Name exists in name map");
734  return i->second->m_object;
735  }
736 }
737 
738 NameNode*
740 {
741  NS_LOG_FUNCTION(this << object);
742 
743  auto i = m_objectMap.find(object);
744  if (i == m_objectMap.end())
745  {
746  NS_LOG_LOGIC("Object does not exist in object map, returning NameNode 0");
747  return nullptr;
748  }
749  else
750  {
751  NS_LOG_LOGIC("Object exists in object map, returning NameNode " << &i->second);
752  return i->second;
753  }
754 }
755 
756 bool
757 NamesPriv::IsDuplicateName(NameNode* node, std::string name)
758 {
759  NS_LOG_FUNCTION(this << node << name);
760 
761  auto i = node->m_nameMap.find(name);
762  if (i == node->m_nameMap.end())
763  {
764  NS_LOG_LOGIC("Name does not exist in name map");
765  return false;
766  }
767  else
768  {
769  NS_LOG_LOGIC("Name exists in name map");
770  return true;
771  }
772 }
773 
774 void
775 Names::Add(std::string name, Ptr<Object> object)
776 {
777  NS_LOG_FUNCTION(name << object);
778  bool result = NamesPriv::Get()->Add(name, object);
779  NS_ABORT_MSG_UNLESS(result, "Names::Add(): Error adding name " << name);
780 }
781 
782 void
783 Names::Rename(std::string oldpath, std::string newname)
784 {
785  NS_LOG_FUNCTION(oldpath << newname);
786  bool result = NamesPriv::Get()->Rename(oldpath, newname);
787  NS_ABORT_MSG_UNLESS(result, "Names::Rename(): Error renaming " << oldpath << " to " << newname);
788 }
789 
790 void
791 Names::Add(std::string path, std::string name, Ptr<Object> object)
792 {
793  NS_LOG_FUNCTION(path << name << object);
794  bool result = NamesPriv::Get()->Add(path, name, object);
795  NS_ABORT_MSG_UNLESS(result, "Names::Add(): Error adding " << path << " " << name);
796 }
797 
798 void
799 Names::Rename(std::string path, std::string oldname, std::string newname)
800 {
801  NS_LOG_FUNCTION(path << oldname << newname);
802  bool result = NamesPriv::Get()->Rename(path, oldname, newname);
803  NS_ABORT_MSG_UNLESS(result,
804  "Names::Rename (): Error renaming " << path << " " << oldname << " to "
805  << newname);
806 }
807 
808 void
809 Names::Add(Ptr<Object> context, std::string name, Ptr<Object> object)
810 {
811  NS_LOG_FUNCTION(context << name << object);
812  bool result = NamesPriv::Get()->Add(context, name, object);
813  NS_ABORT_MSG_UNLESS(result,
814  "Names::Add(): Error adding name " << name << " under context "
815  << &context);
816 }
817 
818 void
819 Names::Rename(Ptr<Object> context, std::string oldname, std::string newname)
820 {
821  NS_LOG_FUNCTION(context << oldname << newname);
822  bool result = NamesPriv::Get()->Rename(context, oldname, newname);
823  NS_ABORT_MSG_UNLESS(result,
824  "Names::Rename (): Error renaming " << oldname << " to " << newname
825  << " under context " << &context);
826 }
827 
828 std::string
830 {
831  NS_LOG_FUNCTION(object);
832  return NamesPriv::Get()->FindName(object);
833 }
834 
835 std::string
837 {
838  NS_LOG_FUNCTION(object);
839  return NamesPriv::Get()->FindPath(object);
840 }
841 
842 void
844 {
846  return NamesPriv::Get()->Clear();
847 }
848 
850 Names::FindInternal(std::string name)
851 {
852  NS_LOG_FUNCTION(name);
853  return NamesPriv::Get()->Find(name);
854 }
855 
857 Names::FindInternal(std::string path, std::string name)
858 {
859  NS_LOG_FUNCTION(path << name);
860  return NamesPriv::Get()->Find(path, name);
861 }
862 
864 Names::FindInternal(Ptr<Object> context, std::string name)
865 {
866  NS_LOG_FUNCTION(context << name);
867  return NamesPriv::Get()->Find(context, name);
868 }
869 
870 } // namespace ns3
NS_ABORT_x macro definitions.
NS_ASSERT() and NS_ASSERT_MSG() macro definitions.
Node in the naming tree.
Definition: names.cc:44
NameNode * m_parent
The parent NameNode.
Definition: names.cc:74
~NameNode()
Destructor.
Definition: names.cc:117
NameNode()
Default constructor.
Definition: names.cc:84
Ptr< Object > m_object
The object corresponding to this NameNode.
Definition: names.cc:78
std::map< std::string, NameNode * > m_nameMap
Children of this NameNode.
Definition: names.cc:81
NameNode & operator=(const NameNode &rhs)
Assignment operator.
Definition: names.cc:100
std::string m_name
The name of this NameNode.
Definition: names.cc:76
static void Rename(std::string oldpath, std::string newname)
Rename a previously associated name.
Definition: names.cc:783
static Ptr< Object > FindInternal(std::string path)
Non-templated internal version of Names::Find.
Definition: names.cc:850
static void Add(std::string name, Ptr< Object > object)
Add the association between the string "name" and the Ptr<Object> obj.
Definition: names.cc:775
static void Clear()
Clear the list of objects associated with names.
Definition: names.cc:843
static std::string FindName(Ptr< Object > object)
Given a pointer to an object, look to see if that object has a name associated with it and,...
Definition: names.cc:829
static std::string FindPath(Ptr< Object > object)
Given a pointer to an object, look to see if that object has a name associated with it and return the...
Definition: names.cc:836
The singleton root Names object.
Definition: names.cc:127
bool Add(std::string name, Ptr< Object > object)
Internal implementation for Names::Add(std::string,Ptr<Object>)
Definition: names.cc:321
NameNode m_root
The root NameNode.
Definition: names.cc:276
bool IsDuplicateName(NameNode *node, std::string name)
Check if a name already exists as a child of a NameNode.
Definition: names.cc:757
std::string FindPath(Ptr< Object > object)
Internal implementation of Names::FindPath()
Definition: names.cc:571
bool Rename(std::string oldpath, std::string newname)
Internal implementation for Names::Rename(std::string,std::string)
Definition: names.cc:430
std::string FindName(Ptr< Object > object)
Internal implementation for Names::FindName()
Definition: names.cc:553
~NamesPriv() override
Destructor.
Definition: names.cc:291
NameNode * IsNamed(Ptr< Object > object)
Check if an object has a name.
Definition: names.cc:739
std::map< Ptr< Object >, NameNode * > m_objectMap
Map from object pointers to their NameNodes.
Definition: names.cc:279
NamesPriv()
Constructor.
Definition: names.cc:282
Ptr< Object > Find(std::string path)
Internal implementation for ns3::Names::Find(std::string)
Definition: names.cc:598
void Clear()
Internal implementation for Names::Clear()
Definition: names.cc:299
A template singleton.
Definition: singleton.h:61
static NamesPriv * Get()
Get a pointer to the singleton instance.
Definition: singleton.h:100
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition: abort.h:144
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Debug message logging.
Declaration of class ns3::Names.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::Object class declaration, which is the root of the Object hierarchy and Aggregation.
ns3::Singleton declaration and template implementation.