From 7514b453f0ddde8ea03e5fc9476ecef1209aa355 Mon Sep 17 00:00:00 2001 From: Jared Dulmage Date: Tue, 25 Aug 2020 16:08:06 -0600 Subject: [PATCH] config-store: (merges !93) Address review comments for RawTextConfig MR - Added documentation to raw-text-config.h - Fixed abort conditions in raw-text-config.cc, RawTextConfigLoad::Strip - Fixed formatting of ParseLine block in load functions - Added value.clear() after setting attribute - Added --load option to config-store-save example, removed config-store-load - Updated comments, doxygen docs, and example help text. --- .../examples/config-store-load.cc | 133 ------------------ .../examples/config-store-save.cc | 27 ++++ src/config-store/examples/wscript | 2 - src/config-store/model/raw-text-config.cc | 23 ++- src/config-store/model/raw-text-config.h | 22 ++- 5 files changed, 62 insertions(+), 145 deletions(-) delete mode 100644 src/config-store/examples/config-store-load.cc diff --git a/src/config-store/examples/config-store-load.cc b/src/config-store/examples/config-store-load.cc deleted file mode 100644 index 1ec3d8405..000000000 --- a/src/config-store/examples/config-store-load.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ -/* - * Copyright (c) 2019, Caliola Engineering, LLC. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation; - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Jared Dulmage - */ -#include "ns3/core-module.h" -#include "ns3/config-store-module.h" -#include "ns3/type-id.h" - -#include - -using namespace ns3; - -/** - * \ingroup configstore-examples - * \ingroup examples - * - * \brief Example class to demonstrate use of the ns-3 Config Store - */ -class ConfigExample : public Object -{ -public: - /** - * \brief Get the type ID. - * \return the object TypeId - */ - static TypeId GetTypeId (void) { - static TypeId tid = TypeId ("ns3::ConfigExample") - .SetParent () - .AddAttribute ("TestInt16", "help text", - IntegerValue (-2), - MakeIntegerAccessor (&ConfigExample::m_int16), - MakeIntegerChecker ()) - ; - return tid; - } - int16_t m_int16; ///< value to configure -}; - -NS_OBJECT_ENSURE_REGISTERED (ConfigExample); - -// Load a ConfigStore file previously saved with config-store-save example. -// Set the filename via --ns3::ConfigStore::Filename=. -// May edit text files to explore comments and blank lines, multi-line -// values. -// Observe behavior with NS_LOG=RawTextConfigLoad. -// -int main (int argc, char *argv[]) -{ - std::string filename; - - CommandLine cmd; - cmd.Usage ("Basic usage: ./waf --run \"config-store-load \""); - cmd.AddNonOption ("filename", "Relative path to config-store input file", filename); - cmd.Parse (argc, argv); - - if (filename.empty ()) - { - std::ostringstream oss; - cmd.PrintHelp (oss); - NS_FATAL_ERROR ("Missing required filename argument" << std::endl << std::endl << oss.str ()); - return 1; - } - Config::SetDefault ("ns3::ConfigStore::Filename", StringValue (filename)); - - Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5)); - - Ptr a_obj = CreateObject (); - NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5, "Cannot set ConfigExample's integer attribute via Config::SetDefault"); - - Ptr b_obj = CreateObject (); - b_obj->SetAttribute ("TestInt16", IntegerValue (-3)); - IntegerValue iv; - b_obj->GetAttribute ("TestInt16", iv); - NS_ABORT_MSG_UNLESS (iv.Get () == -3, "Cannot set ConfigExample's integer attribute via SetAttribute"); - - // These test objects are not rooted in any ns-3 configuration namespace. - // This is usually done automatically for ns3 nodes and channels, but - // we can establish a new root and anchor one of them there (note; we - // can't use two objects of the same type as roots). Rooting one of these - // is necessary for it to show up in the config namespace so that - // ConfigureAttributes() will work below. - Config::RegisterRootNamespaceObject (b_obj); - - std::string::size_type pos = filename.rfind ('.'); - NS_ABORT_MSG_IF (pos == filename.npos, "Could not find filename extension for " << filename); - - std::string ext = filename.substr (pos); - // Input config store from XML format - if (ext == ".xml") - { -#ifndef HAVE_LIBXML2 - NS_FATAL_ERROR ("No built-in XML library support"); -#else - Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml")); - Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load")); - ConfigStore inputConfig; - inputConfig.ConfigureDefaults (); - inputConfig.ConfigureAttributes (); -#endif /* HAVE_LIBXML2 */ - } - else if (ext == ".txt") - { - // Input config store from txt format - Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText")); - Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load")); - ConfigStore inputConfig; - inputConfig.ConfigureDefaults (); - inputConfig.ConfigureAttributes (); - } - else - { - NS_FATAL_ERROR ("Unsupported extension " << ext << " of filename " << filename); - } - - Simulator::Run (); - - Simulator::Destroy (); -} diff --git a/src/config-store/examples/config-store-save.cc b/src/config-store/examples/config-store-save.cc index 64e26aae6..c0ae4ecd0 100644 --- a/src/config-store/examples/config-store-save.cc +++ b/src/config-store/examples/config-store-save.cc @@ -40,9 +40,36 @@ NS_OBJECT_ENSURE_REGISTERED (ConfigExample); // int main (int argc, char *argv[]) { + std::string loadfile; + CommandLine cmd (__FILE__); + cmd.Usage ("Without arguments, write out ConfigStore defaults, globals, and\n" + "test object ConfigExample attributes to text file output-attributes.txt\n" + "and (when XML supported) output-attributes.xml. Optionally set\n" + "attributes to write out using --load where is a\n" + "previously saved config-store file to load.\n" + "Observe load behavior by setting environment variable NS_LOG=RawTextConfig." + ); + cmd.AddValue ("load", "Relative path to config-store input file", loadfile); cmd.Parse (argc, argv); + if (!loadfile.empty ()) + { + Config::SetDefault ("ns3::ConfigStore::Filename", StringValue (loadfile)); + if (loadfile.substr(loadfile.size() - 4) == ".xml") + { + Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml")); + } + else + { + Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText")); + } + Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load")); + ConfigStore loadConfig; + loadConfig.ConfigureDefaults(); + loadConfig.ConfigureAttributes(); + } + Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5)); Ptr a_obj = CreateObject (); diff --git a/src/config-store/examples/wscript b/src/config-store/examples/wscript index 5510233cc..7bacf5b35 100644 --- a/src/config-store/examples/wscript +++ b/src/config-store/examples/wscript @@ -4,5 +4,3 @@ def build(bld): obj = bld.create_ns3_program('config-store-save', ['core', 'config-store']) obj.source = 'config-store-save.cc' - obj = bld.create_ns3_program('config-store-load', ['core', 'config-store']) - obj.source = 'config-store-load.cc' diff --git a/src/config-store/model/raw-text-config.cc b/src/config-store/model/raw-text-config.cc index 9bc09ecdd..2499a6c12 100644 --- a/src/config-store/model/raw-text-config.cc +++ b/src/config-store/model/raw-text-config.cc @@ -145,8 +145,8 @@ RawTextConfigLoad::Strip (std::string value) NS_LOG_FUNCTION (this << value); std::string::size_type start = value.find ("\""); std::string::size_type end = value.find ("\"", 1); - NS_ABORT_MSG_IF (start == 0, "Ill-formed attribute value: " << value); - NS_ABORT_MSG_IF (end == value.size () - 1, "Ill-formed attribute value: " << value); + NS_ABORT_MSG_IF (start != 0, "Ill-formed attribute value: " << value); + NS_ABORT_MSG_IF (end != value.size () - 1, "Ill-formed attribute value: " << value); return value.substr (start+1, end-start-1); } @@ -159,7 +159,10 @@ RawTextConfigLoad::Default (void) std::string type, name, value; for (std::string line; std::getline (*m_is, line);) { - if (!ParseLine (line, type, name, value)) continue; // comment + if (!ParseLine (line, type, name, value)) + { + continue; + } NS_LOG_DEBUG ("type=" << type << ", name=" << name << ", value=" << value); value = Strip (value); @@ -169,6 +172,7 @@ RawTextConfigLoad::Default (void) } name.clear (); type.clear (); + value.clear (); } } void @@ -182,7 +186,7 @@ RawTextConfigLoad::Global (void) { if (!ParseLine (line, type, name, value)) { - continue; // comment + continue; } NS_LOG_DEBUG ("type=" << type << ", name=" << name << ", value=" << value); @@ -193,6 +197,7 @@ RawTextConfigLoad::Global (void) } name.clear (); type.clear (); + value.clear (); } } void @@ -204,7 +209,10 @@ RawTextConfigLoad::Attributes (void) std::string type, name, value; for (std::string line; std::getline (*m_is, line);) { - if (!ParseLine (line, type, name, value)) continue; + if (!ParseLine (line, type, name, value)) + { + continue; + } NS_LOG_DEBUG ("type=" << type << ", name=" << name << ", value=" << value); value = Strip (value); @@ -214,6 +222,7 @@ RawTextConfigLoad::Attributes (void) } name.clear (); type.clear (); + value.clear (); } } @@ -249,7 +258,9 @@ RawTextConfigLoad::ParseLine (const std::string &line, std::string &type, std::s value.append (line); } - // look for two quotes + // two quotes in value signifies a completed (possibly multi-line) + // config-store entry, return True to signal load function to + // validate value (see Strip method) and set attribute return std::count (value.begin (), value.end (), '"') == 2; } diff --git a/src/config-store/model/raw-text-config.h b/src/config-store/model/raw-text-config.h index e005531a7..2e1ab206e 100644 --- a/src/config-store/model/raw-text-config.h +++ b/src/config-store/model/raw-text-config.h @@ -35,8 +35,9 @@ namespace ns3 { class RawTextConfigSave : public FileConfig { public: - RawTextConfigSave (); - virtual ~RawTextConfigSave (); + RawTextConfigSave (); //!< default constructor + virtual ~RawTextConfigSave (); //!< destructor + // Inherited virtual void SetFilename (std::string filename); virtual void Default (void); virtual void Global (void); @@ -54,13 +55,26 @@ private: class RawTextConfigLoad : public FileConfig { public: - RawTextConfigLoad (); - virtual ~RawTextConfigLoad (); + RawTextConfigLoad (); //!< default constructor + virtual ~RawTextConfigLoad (); //!< destructor + // Inherited virtual void SetFilename (std::string filename); virtual void Default (void); virtual void Global (void); virtual void Attributes (void); private: + /** + * Parse (potentially multi-) line configs into type, name, and values. + * This will return \c false for blank lines, comments (lines beginning with '#'), + * and incomplete entries. An entry is considered complete when a type and name + * have been found and the value contains two delimiting quotation marks '"'. + * \param line the config file line + * \param type the config type {default, global, value} + * \param name the config attribute name + * \param value the value to set + * \returns true if all of type, name, and value parsed; false otherwise + * + */ virtual bool ParseLine (const std::string &line, std::string &type, std::string &name, std::string &value); /**