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 (std::map<Ptr<Object>, NameNode*>::iterator i = m_objectMap.begin(); i != m_objectMap.end();
307  ++i)
308  {
309  delete i->second;
310  i->second = 0;
311  }
312 
313  m_objectMap.clear();
314 
315  m_root.m_parent = nullptr;
316  m_root.m_name = "Names";
317  m_root.m_object = nullptr;
318  m_root.m_nameMap.clear();
319 }
320 
321 bool
322 NamesPriv::Add(std::string name, Ptr<Object> object)
323 {
324  NS_LOG_FUNCTION(this << name << object);
325  //
326  // This is the simple, easy to use version of Add, so we want it to be flexible.
327  // We don't want to force a user to always type the fully qualified namespace
328  // name, so we allow the namespace name to be omitted. For example, calling
329  // Add ("Client/ath0", obj) should result in exactly the same behavior as
330  // Add ("/Names/Client/ath0", obj). Calling Add ("Client", obj) should have
331  // the same effect as Add ("Names/Client", obj)
332  //
333  // The first thing to do, then, is to "canonicalize" the input string to always
334  // be a fully qualified name.
335  //
336  // If we are given a name that begins with "/Names/" we assume that this is a
337  // fully qualified path name to the object we want to create. We split the name
338  // into a path string and a final segment (name) and then call the "Real" Add.
339  //
340  std::string namespaceName = "/Names";
341  std::string::size_type offset = name.find(namespaceName);
342  if (offset != 0)
343  {
344  //
345  // This must be a name that has the "/Names" namespace prefix omitted.
346  // Do some reasonableness checking on the rest of the name.
347  //
348  offset = name.find('/');
349  if (offset == 0)
350  {
351  NS_ASSERT_MSG(false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
352  return false;
353  }
354 
355  name = "/Names/" + name;
356  }
357 
358  //
359  // There must now be a fully qualified path in the string. All fully
360  // qualified names begin with "/Names". We have to split off the final
361  // segment which will become the name of the object. A '/' that
362  // separates the path from the final segment had better be there since
363  // we just made sure that at least the namespace name was there.
364  //
365  std::string::size_type i = name.rfind('/');
366  NS_ASSERT_MSG(i != std::string::npos,
367  "NamesPriv::Add(): Internal error. Can't find '/' in name");
368 
369  //
370  // The slash we found cannot be the slash at the start of the namespaceName.
371  // This would indicate there is no name in the path at all. It can be
372  // any other index.
373  //
374  NS_ASSERT_MSG(i != 0, "NamesPriv::Add(): Can't find a name in the path string");
375 
376  //
377  // We now know where the path string starts and ends, and where the
378  // name starts and ends. All we have to do is to call our available
379  // function for adding a name under a path string.
380  //
381  return Add(name.substr(0, i), name.substr(i + 1), object);
382 }
383 
384 bool
385 NamesPriv::Add(std::string path, std::string name, Ptr<Object> object)
386 {
387  NS_LOG_FUNCTION(this << path << name << object);
388  if (path == "/Names")
389  {
390  return Add(Ptr<Object>(nullptr, false), name, object);
391  }
392  return Add(Find(path), name, object);
393 }
394 
395 bool
396 NamesPriv::Add(Ptr<Object> context, std::string name, Ptr<Object> object)
397 {
398  NS_LOG_FUNCTION(this << context << name << object);
399 
400  if (IsNamed(object))
401  {
402  NS_LOG_LOGIC("Object is already named");
403  return false;
404  }
405 
406  NameNode* node = nullptr;
407  if (context)
408  {
409  node = IsNamed(context);
410  NS_ASSERT_MSG(node, "NamesPriv::Name(): context must point to a previously named node");
411  }
412  else
413  {
414  node = &m_root;
415  }
416 
417  if (IsDuplicateName(node, name))
418  {
419  NS_LOG_LOGIC("Name is already taken");
420  return false;
421  }
422 
423  NameNode* newNode = new NameNode(node, name, object);
424  node->m_nameMap[name] = newNode;
425  m_objectMap[object] = newNode;
426 
427  return true;
428 }
429 
430 bool
431 NamesPriv::Rename(std::string oldpath, std::string newname)
432 {
433  NS_LOG_FUNCTION(this << oldpath << newname);
434  //
435  // This is the simple, easy to use version of Rename, so we want it to be
436  // flexible. We don't want to force a user to always type the fully
437  // qualified namespace name, so we allow the namespace name to be omitted.
438  // For example, calling Rename ("Client/ath0", "eth0") should result in
439  // exactly the same behavior as Rename ("/Names/Client/ath0", "eth0").
440  // Calling Rename ("Client", "Router") should have the same effect as
441  // Rename ("Names/Client", "Router")
442  //
443  // The first thing to do, then, is to "canonicalize" the input string to always
444  // be a fully qualified path.
445  //
446  // If we are given a name that begins with "/Names/" we assume that this is a
447  // fully qualified path to the object we want to change. We split the path into
448  // path string (cf directory) and a final segment (cf filename) and then call
449  // the "Real" Rename.
450  //
451  std::string namespaceName = "/Names";
452  std::string::size_type offset = oldpath.find(namespaceName);
453  if (offset != 0)
454  {
455  //
456  // This must be a name that has the "/Names" namespace prefix omitted.
457  // Do some reasonableness checking on the rest of the name.
458  //
459  offset = oldpath.find('/');
460  if (offset == 0)
461  {
462  NS_ASSERT_MSG(false, "NamesPriv::Add(): Name begins with '/' but not \"/Names\"");
463  return false;
464  }
465 
466  oldpath = "/Names/" + oldpath;
467  }
468 
469  //
470  // There must now be a fully qualified path in the oldpath string. All
471  // fully qualified names begin with "/Names". We have to split off the final
472  // segment which will become the name we want to rename. A '/' that
473  // separates the path from the final segment (name) had better be there since
474  // we just made sure that at least the namespace name was there.
475  //
476  std::string::size_type i = oldpath.rfind('/');
477  NS_ASSERT_MSG(i != std::string::npos,
478  "NamesPriv::Add(): Internal error. Can't find '/' in name");
479 
480  //
481  // The slash we found cannot be the slash at the start of the namespaceName.
482  // This would indicate there is no name in the path at all. It can be
483  // any other index.
484  //
485  NS_ASSERT_MSG(i != 0, "NamesPriv::Add(): Can't find a name in the path string");
486 
487  //
488  // We now know where the path part of the string starts and ends, and where the
489  // name part starts and ends. All we have to do is to call our available
490  // function for creating adding a name under a path string.
491  //
492  return Rename(oldpath.substr(0, i), oldpath.substr(i + 1), newname);
493 }
494 
495 bool
496 NamesPriv::Rename(std::string path, std::string oldname, std::string newname)
497 {
498  NS_LOG_FUNCTION(this << path << oldname << newname);
499  if (path == "/Names")
500  {
501  return Rename(Ptr<Object>(nullptr, false), oldname, newname);
502  }
503  return Rename(Find(path), oldname, newname);
504 }
505 
506 bool
507 NamesPriv::Rename(Ptr<Object> context, std::string oldname, std::string newname)
508 {
509  NS_LOG_FUNCTION(this << context << oldname << newname);
510 
511  NameNode* node = nullptr;
512  if (context)
513  {
514  node = IsNamed(context);
515  NS_ASSERT_MSG(node, "NamesPriv::Name(): context must point to a previously named node");
516  }
517  else
518  {
519  node = &m_root;
520  }
521 
522  if (IsDuplicateName(node, newname))
523  {
524  NS_LOG_LOGIC("New name is already taken");
525  return false;
526  }
527 
528  std::map<std::string, NameNode*>::iterator i = node->m_nameMap.find(oldname);
529  if (i == node->m_nameMap.end())
530  {
531  NS_LOG_LOGIC("Old name does not exist in name map");
532  return false;
533  }
534  else
535  {
536  NS_LOG_LOGIC("Old name exists in name map");
537 
538  //
539  // The rename process consists of:
540  // 1. Getting the pointer to the name node from the map and remembering it;
541  // 2. Removing the map entry corresponding to oldname from the map;
542  // 3. Changing the name string in the name node;
543  // 4. Adding the name node back in the map under the newname.
544  //
545  NameNode* changeNode = i->second;
546  node->m_nameMap.erase(i);
547  changeNode->m_name = newname;
548  node->m_nameMap[newname] = changeNode;
549  return true;
550  }
551 }
552 
553 std::string
555 {
556  NS_LOG_FUNCTION(this << object);
557 
558  std::map<Ptr<Object>, NameNode*>::iterator i = m_objectMap.find(object);
559  if (i == m_objectMap.end())
560  {
561  NS_LOG_LOGIC("Object does not exist in object map");
562  return "";
563  }
564  else
565  {
566  NS_LOG_LOGIC("Object exists in object map");
567  return i->second->m_name;
568  }
569 }
570 
571 std::string
573 {
574  NS_LOG_FUNCTION(this << object);
575 
576  std::map<Ptr<Object>, NameNode*>::iterator i = m_objectMap.find(object);
577  if (i == m_objectMap.end())
578  {
579  NS_LOG_LOGIC("Object does not exist in object map");
580  return "";
581  }
582 
583  NameNode* p = i->second;
584  NS_ASSERT_MSG(p,
585  "NamesPriv::FindFullName(): Internal error: Invalid NameNode pointer from map");
586 
587  std::string path;
588 
589  do
590  {
591  path = "/" + p->m_name + path;
592  NS_LOG_LOGIC("path is " << path);
593  } while ((p = p->m_parent) != nullptr);
594 
595  return path;
596 }
597 
599 NamesPriv::Find(std::string path)
600 {
601  //
602  // This is hooked in from simple, easy to use version of Find, so we want it
603  // to be flexible.
604  //
605  // If we are provided a path that doesn't begin with "/Names", we assume
606  // that the caller has simply given us a path starting with a name that
607  // is in the root namespace. This allows people to omit the "/Names" prefix.
608  // and simply do a Find ("Client/eth0") instead of having to always do a
609  // Find ("/Names/Client/eth0");
610  //
611  // So, if we are given a name that begins with "/Names/" the upshot is that we
612  // just remove that prefix and treat the rest of the string as starting with a
613  // name in the root namespace.
614  //
615 
616  NS_LOG_FUNCTION(this << path);
617  std::string namespaceName = "/Names/";
618  std::string remaining;
619 
620  std::string::size_type offset = path.find(namespaceName);
621  if (offset == 0)
622  {
623  NS_LOG_LOGIC(path << " is a fully qualified name");
624  remaining = path.substr(namespaceName.size());
625  }
626  else
627  {
628  NS_LOG_LOGIC(path << " begins with a relative name");
629  remaining = path;
630  }
631 
632  NameNode* node = &m_root;
633 
634  //
635  // The string <remaining> is now composed entirely of path segments in
636  // the /Names name space and we have eaten the leading slash. e.g.,
637  // remaining = "ClientNode/eth0"
638  //
639  // The start of the search is always at the root of the name space.
640  //
641  for (;;)
642  {
643  NS_LOG_LOGIC("Looking for the object of name " << remaining);
644  offset = remaining.find('/');
645  if (offset == std::string::npos)
646  {
647  //
648  // There are no remaining slashes so this is the last segment of the
649  // specified name. We're done when we find it
650  //
651  std::map<std::string, NameNode*>::iterator i = node->m_nameMap.find(remaining);
652  if (i == node->m_nameMap.end())
653  {
654  NS_LOG_LOGIC("Name does not exist in name map");
655  return nullptr;
656  }
657  else
658  {
659  NS_LOG_LOGIC("Name parsed, found object");
660  return i->second->m_object;
661  }
662  }
663  else
664  {
665  //
666  // There are more slashes so this is an intermediate segment of the
667  // specified name. We need to "recurse" when we find this segment.
668  //
669  offset = remaining.find('/');
670  std::string segment = remaining.substr(0, offset);
671 
672  std::map<std::string, NameNode*>::iterator i = node->m_nameMap.find(segment);
673  if (i == node->m_nameMap.end())
674  {
675  NS_LOG_LOGIC("Name does not exist in name map");
676  return nullptr;
677  }
678  else
679  {
680  node = i->second;
681  remaining = remaining.substr(offset + 1);
682  NS_LOG_LOGIC("Intermediate segment parsed");
683  continue;
684  }
685  }
686  }
687 
688  NS_ASSERT_MSG(node, "NamesPriv::Find(): Internal error: this can't happen");
689  return nullptr;
690 }
691 
693 NamesPriv::Find(std::string path, std::string name)
694 {
695  NS_LOG_FUNCTION(this << path << name);
696 
697  if (path == "/Names")
698  {
699  return Find(Ptr<Object>(nullptr, false), name);
700  }
701  return Find(Find(path), name);
702 }
703 
705 NamesPriv::Find(Ptr<Object> context, std::string name)
706 {
707  NS_LOG_FUNCTION(this << context << name);
708 
709  NameNode* node = nullptr;
710 
711  if (!context)
712  {
713  NS_LOG_LOGIC("Zero context implies root NameNode");
714  node = &m_root;
715  }
716  else
717  {
718  node = IsNamed(context);
719  if (node == nullptr)
720  {
721  NS_LOG_LOGIC("Context does not point to a previously named node");
722  return nullptr;
723  }
724  }
725 
726  std::map<std::string, NameNode*>::iterator i = node->m_nameMap.find(name);
727  if (i == node->m_nameMap.end())
728  {
729  NS_LOG_LOGIC("Name does not exist in name map");
730  return nullptr;
731  }
732  else
733  {
734  NS_LOG_LOGIC("Name exists in name map");
735  return i->second->m_object;
736  }
737 }
738 
739 NameNode*
741 {
742  NS_LOG_FUNCTION(this << object);
743 
744  std::map<Ptr<Object>, NameNode*>::iterator i = m_objectMap.find(object);
745  if (i == m_objectMap.end())
746  {
747  NS_LOG_LOGIC("Object does not exist in object map, returning NameNode 0");
748  return nullptr;
749  }
750  else
751  {
752  NS_LOG_LOGIC("Object exists in object map, returning NameNode " << &i->second);
753  return i->second;
754  }
755 }
756 
757 bool
758 NamesPriv::IsDuplicateName(NameNode* node, std::string name)
759 {
760  NS_LOG_FUNCTION(this << node << name);
761 
762  std::map<std::string, NameNode*>::iterator i = node->m_nameMap.find(name);
763  if (i == node->m_nameMap.end())
764  {
765  NS_LOG_LOGIC("Name does not exist in name map");
766  return false;
767  }
768  else
769  {
770  NS_LOG_LOGIC("Name exists in name map");
771  return true;
772  }
773 }
774 
775 void
776 Names::Add(std::string name, Ptr<Object> object)
777 {
778  NS_LOG_FUNCTION(name << object);
779  bool result = NamesPriv::Get()->Add(name, object);
780  NS_ABORT_MSG_UNLESS(result, "Names::Add(): Error adding name " << name);
781 }
782 
783 void
784 Names::Rename(std::string oldpath, std::string newname)
785 {
786  NS_LOG_FUNCTION(oldpath << newname);
787  bool result = NamesPriv::Get()->Rename(oldpath, newname);
788  NS_ABORT_MSG_UNLESS(result, "Names::Rename(): Error renaming " << oldpath << " to " << newname);
789 }
790 
791 void
792 Names::Add(std::string path, std::string name, Ptr<Object> object)
793 {
794  NS_LOG_FUNCTION(path << name << object);
795  bool result = NamesPriv::Get()->Add(path, name, object);
796  NS_ABORT_MSG_UNLESS(result, "Names::Add(): Error adding " << path << " " << name);
797 }
798 
799 void
800 Names::Rename(std::string path, std::string oldname, std::string newname)
801 {
802  NS_LOG_FUNCTION(path << oldname << newname);
803  bool result = NamesPriv::Get()->Rename(path, oldname, newname);
804  NS_ABORT_MSG_UNLESS(result,
805  "Names::Rename (): Error renaming " << path << " " << oldname << " to "
806  << newname);
807 }
808 
809 void
810 Names::Add(Ptr<Object> context, std::string name, Ptr<Object> object)
811 {
812  NS_LOG_FUNCTION(context << name << object);
813  bool result = NamesPriv::Get()->Add(context, name, object);
814  NS_ABORT_MSG_UNLESS(result,
815  "Names::Add(): Error adding name " << name << " under context "
816  << &context);
817 }
818 
819 void
820 Names::Rename(Ptr<Object> context, std::string oldname, std::string newname)
821 {
822  NS_LOG_FUNCTION(context << oldname << newname);
823  bool result = NamesPriv::Get()->Rename(context, oldname, newname);
824  NS_ABORT_MSG_UNLESS(result,
825  "Names::Rename (): Error renaming " << oldname << " to " << newname
826  << " under context " << &context);
827 }
828 
829 std::string
831 {
832  NS_LOG_FUNCTION(object);
833  return NamesPriv::Get()->FindName(object);
834 }
835 
836 std::string
838 {
839  NS_LOG_FUNCTION(object);
840  return NamesPriv::Get()->FindPath(object);
841 }
842 
843 void
845 {
847  return NamesPriv::Get()->Clear();
848 }
849 
851 Names::FindInternal(std::string name)
852 {
853  NS_LOG_FUNCTION(name);
854  return NamesPriv::Get()->Find(name);
855 }
856 
858 Names::FindInternal(std::string path, std::string name)
859 {
860  NS_LOG_FUNCTION(path << name);
861  return NamesPriv::Get()->Find(path, name);
862 }
863 
865 Names::FindInternal(Ptr<Object> context, std::string name)
866 {
867  NS_LOG_FUNCTION(context << name);
868  return NamesPriv::Get()->Find(context, name);
869 }
870 
871 } // 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:784
static Ptr< Object > FindInternal(std::string path)
Non-templated internal version of Names::Find.
Definition: names.cc:851
static void Add(std::string name, Ptr< Object > object)
Add the association between the string "name" and the Ptr<Object> obj.
Definition: names.cc:776
static void Clear()
Clear the list of objects associated with names.
Definition: names.cc:844
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:830
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:837
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:322
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:758
std::string FindPath(Ptr< Object > object)
Internal implementation of Names::FindPath()
Definition: names.cc:572
bool Rename(std::string oldpath, std::string newname)
Internal implementation for Names::Rename(std::string,std::string)
Definition: names.cc:431
std::string FindName(Ptr< Object > object)
Internal implementation for Names::FindName()
Definition: names.cc:554
~NamesPriv() override
Destructor.
Definition: names.cc:291
NameNode * IsNamed(Ptr< Object > object)
Check if an object has a name.
Definition: names.cc:740
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:599
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.