diff --git a/src/core/examples/command-line-example.cc b/src/core/examples/command-line-example.cc new file mode 100644 index 000000000..1104e60ec --- /dev/null +++ b/src/core/examples/command-line-example.cc @@ -0,0 +1,48 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 INRIA + * + * 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: Mathieu Lacage + */ + +#include + +#include "ns3/core-module.h" + +using namespace ns3; + +int main (int argc, char *argv[]) +{ + + int val1 = 1; + bool val2 = false; + std::string val3 = ""; + + CommandLine cmd; + cmd.Usage ("CommandLine example program.\n" + "\n" + "This little program demonstrates how to use CommandLine."); + cmd.AddValue ("val1", "an int argument", val1); + cmd.AddValue ("val2", "a bool argument", val2); + cmd.AddValue ("val3", "a string argument", val3); + cmd.Parse (argc, argv); + + std::cout << "val1:\t" << val1 << std::endl; + std::cout << "val2:\t" << val2 << std::endl; + std::cout << "val3:\t\"" << val3 << "\"" << std::endl; + + return 0; +} diff --git a/src/core/examples/wscript b/src/core/examples/wscript index a4da402ed..da4c4e811 100644 --- a/src/core/examples/wscript +++ b/src/core/examples/wscript @@ -29,6 +29,10 @@ def build(bld): ['core']) obj.source = 'sample-random-variable-stream.cc' + obj = bld.create_ns3_program('command-line-example', + ['core']) + obj.source = 'command-line-example.cc' + if bld.env['ENABLE_THREADING'] and bld.env["ENABLE_REAL_TIME"]: obj = bld.create_ns3_program('main-test-sync', ['network']) obj.source = 'main-test-sync.cc' diff --git a/src/core/model/command-line.cc b/src/core/model/command-line.cc index c8c634e03..ab1ab4704 100644 --- a/src/core/model/command-line.cc +++ b/src/core/model/command-line.cc @@ -18,12 +18,15 @@ * Authors: Mathieu Lacage */ -#include // for exit +#include // for transform +#include // for tolower +#include // for exit #include "command-line.h" #include "log.h" #include "config.h" #include "global-value.h" +#include "system-path.h" #include "type-id.h" #include "string.h" @@ -62,6 +65,8 @@ CommandLine::Copy (const CommandLine &cmd) { m_items.push_back (*i); } + m_usage = cmd.m_usage; + m_name = cmd.m_name; } void CommandLine::Clear (void) @@ -73,6 +78,20 @@ CommandLine::Clear (void) delete *i; } m_items.clear (); + m_usage = ""; + m_name = ""; +} + +void +CommandLine::Usage (const std::string usage) +{ + m_usage = usage; +} + +std::string +CommandLine::GetName () const +{ + return m_name; } CommandLine::Item::~Item () @@ -81,10 +100,12 @@ CommandLine::Item::~Item () } void -CommandLine::Parse (int iargc, char *argv[]) const +CommandLine::Parse (int iargc, char *argv[]) { NS_LOG_FUNCTION (this << iargc << argv); + m_name = SystemPath::Split (argv[0]).back (); + int argc = iargc; for (argc--, argv++; argc > 0; argc--, argv++) { @@ -129,20 +150,34 @@ CommandLine::PrintHelp (void) const { NS_LOG_FUNCTION (this); - std::cout << "--PrintHelp: Print this help message." << std::endl; - std::cout << "--PrintGroups: Print the list of groups." << std::endl; - std::cout << "--PrintTypeIds: Print all TypeIds." << std::endl; - std::cout << "--PrintGroup=[group]: Print all TypeIds of group." << std::endl; - std::cout << "--PrintAttributes=[typeid]: Print all attributes of typeid." << std::endl; - std::cout << "--PrintGlobals: Print the list of globals." << std::endl; + std::cout << m_name << " [Program Arguments] [General Arguments]" << std::endl; + + if (m_usage.length ()) + { + std::cout << std::endl; + std::cout << m_usage << std::endl; + } + if (!m_items.empty ()) { - std::cout << "User Arguments:" << std::endl; + std::cout << std::endl; + std::cout << "Program Arguments:" << std::endl; for (Items::const_iterator i = m_items.begin (); i != m_items.end (); ++i) { - std::cout << " --" << (*i)->m_name << ": " << (*i)->m_help << std::endl; + std::cout << " --" << (*i)->m_name << ":\t" << (*i)->m_help << std::endl; } } + + std::cout << std::endl; + std::cout + << "General Arguments:\n" + << " --PrintHelp: Print this help message.\n" + << " --PrintGroups: Print the list of groups.\n" + << " --PrintTypeIds: Print all TypeIds.\n" + << " --PrintGroup=[group]: Print all TypeIds of group.\n" + << " --PrintAttributes=[typeid]: Print all attributes of typeid.\n" + << " --PrintGlobals: Print the list of globals.\n" + << std::endl; } void @@ -191,7 +226,7 @@ CommandLine::PrintGroup (std::string group) const TypeId tid = TypeId::GetRegistered (i); if (tid.GetGroupName () == group) { - std::cout << " --PrintAttributes=" < +bool +CommandLineHelper::UserItemParse (const std::string value, bool & val) +{ + std::string src = value; + std::transform(src.begin(), src.end(), src.begin(), ::tolower); + + if ( (value.length () == 0) || (value == "true") || (value == "t")) + { + val = true; + return true; + } + else + { + std::istringstream iss; + iss.str (value); + iss >> val; + return !iss.bad () && !iss.fail (); + } +} + } // namespace ns3 diff --git a/src/core/model/command-line.h b/src/core/model/command-line.h index 288c029e6..4ecdb9860 100644 --- a/src/core/model/command-line.h +++ b/src/core/model/command-line.h @@ -29,35 +29,121 @@ namespace ns3 { /** - * \brief parse command-line arguments * \ingroup core + * \defgroup commandline Command Line Parsing + * + * A uniform way to specify program documentation, + * allowed command line arguments and help strings, + * and set any attribute or global value, all from + * the command line directly. + * + * The main entry point is CommandLine + */ +/** + * \ingroup commandline + * \brief Parse command-line arguments * * Instances of this class can be used to parse command-line - * arguments: users can register new arguments with - * CommandLine::AddValue but the most important functionality - * provided by this class is that it can be used to set the + * arguments: programs can register new arguments with + * CommandLine::AddValue. + * + * In addition, this class can be used to set the * 'initial value' of every attribute in the system with the - * \verbatim + * \code * --TypeIdName::AttributeName=value - * \endverbatim - * syntax and it can be used to set the value of every GlobalValue + * \endcode + * syntax, and it can be used to set the value of every GlobalValue * in the system with the - * \verbatim + * \code * --GlobalValueName=value - * \endverbatim + * \endcode * syntax. + * + * A simple example is in \c src/core/example/command-line-example.cc + * The heart of that example is this code: + * + * \code + * int val1 = 1; + * bool val2 = false; + * std::string val3 = ""; + * + * CommandLine cmd; + * cmd.Usage ("CommandLine example program.\n" + * "\n" + * "This little program demonstrates how to use CommandLine."); + * cmd.AddValue ("val1", "an int argument", val1); + * cmd.AddValue ("val2", "a bool argument", val2); + * cmd.AddValue ("val3", "a string argument", val3); + * cmd.Parse (argc, argv); + * \endcode + * + * Here is the output from a few runs of that program: + * + * \code + * $ ./waf --run="command-line-example" + * val1: 1 + * val2: 0 + * val3: "" + * + * $ ./waf --run="command-line-example --val1=2 --val2 --val3=Hello" + * val1: 2 + * val2: 1 + * val3: "Hello" + * + * $ ./waf --run="command-line-example --help" + * ns3-dev-command-line-example-debug [Program Arguments] [General Arguments] + * + * CommandLine example program. + * + * This little program demonstrates how to use CommandLine. + * + * Program Arguments: + * --val1: an int argument + * --val2: a bool argument + * --val3: a string argument + * + * General Arguments: + * --PrintHelp: Print this help message. + * --PrintGroups: Print the list of groups. + * --PrintTypeIds: Print all TypeIds. + * --PrintGroup=[group]: Print all TypeIds of group. + * --PrintAttributes=[typeid]: Print all attributes of typeid. + * --PrintGlobals: Print the list of globals. + * \endcode */ class CommandLine { public: + /** Constructor */ CommandLine (); + /** + * Copy constructor + * + * \param cmd the CommandLine to copy from + */ CommandLine (const CommandLine &cmd); + /** + * Assignment + * + * \param cmd the CommandLine to assign from + * \return the CommandLine + */ CommandLine &operator = (const CommandLine &cmd); + /** Destructor */ ~CommandLine (); /** - * \param name the name of the user-supplied argument - * \param help some help text used by --PrintHelp + * Supply the program usage and documentation. + * + * \param usage Program usage message to write with help. + */ + void Usage (const std::string usage); + + /** + * Add a program argument, assigning to POD + * + * \param name the name of the program-supplied argument + * \param help the help text used by \c \-\-PrintHelp * \param value a reference to the variable where the * value parsed will be stored (if no value * is parsed, this variable is not modified). @@ -69,16 +155,20 @@ public: /** - * \param name the name of the user-supplied argument - * \param help some help text used by --PrintHelp - * \param callback a callback function that will be invoked to parse - * and collect the value. This normally used by language bindings. + * Add a program argument, using a Callback to parse the value + * + * \param name the name of the program-supplied argument + * \param help the help text used by \c \-\-PrintHelp + * \param callback a Callback function that will be invoked to parse + * and collect the value. This is normally used by language bindings. */ void AddValue (const std::string &name, const std::string &help, Callback callback); /** + * Parse the program arguments + * * \param argc the 'argc' variable: number of arguments (including the * main program name as first element). * \param argv the 'argv' variable: a null-terminated array of strings, @@ -86,45 +176,145 @@ public: * * Obviously, this method will parse the input command-line arguments and * will attempt to handle them all. + * + * As a side effect, this method saves the program basename, which + * can be retrieved by GetName(). */ - void Parse (int argc, char *argv[]) const; + void Parse (int argc, char *argv[]); + + /** + * Get the program name + * + * \return the program name. Only valid after calling Parse() + */ + std::string GetName () const; + private: + + /** + * \ingroup commandline + * \brief The argument base class + */ class Item { -public: - std::string m_name; - std::string m_help; - virtual ~Item (); + public: + std::string m_name; /**< Argument label: \c \-\--m_name=... */ + std::string m_help; /**< Argument help string */ + virtual ~Item (); /**< Destructor */ + /** + * Parse from a string. + * + * \param value the string representation + * \return true if parsing the value succeeded + */ virtual bool Parse (std::string value) = 0; }; + + /** + * \ingroup commandline + *\brief An argument Item assigning to POD + */ template class UserItem : public Item { -public: + public: + /** + * Parse from a string. + * + * \param value the string representation + * \return true if parsing the value succeeded + */ virtual bool Parse (std::string value); - T *m_valuePtr; + T *m_valuePtr; /**< Pointer to the POD location */ }; + + /** + * \ingroup commandline + * \brief An argument Item using a Callback to parse the input + */ class CallbackItem : public Item { -public: + public: + /** + * Parse from a string. + * + * \param value the string representation + * \return true if parsing the value succeeded + */ virtual bool Parse (std::string value); - Callback m_callback; + Callback m_callback; /**< The Callback */ }; + /** + * Match name against the program or general arguments, + * and dispatch to the appropriate handler. + * + * \param name the argument name + * \param value the command line value + */ void HandleArgument (std::string name, std::string value) const; + /** + * Handler for \c \-\-PrintHelp and \c \-\-help: print Usage(), argument names, and help strings + */ void PrintHelp (void) const; + /** Handler for \c \-\-PrintGlobals: print all global variables and values */ void PrintGlobals (void) const; + /** + * Handler for \c \-\-PrintAttributes: print the attributes for a given type. + * + * \param type the TypeId whose Attributes should be displayed + */ void PrintAttributes (std::string type) const; + /** + * Handler for \c \-\-PrintGroup: print all types belonging to a given group. + * + * \param group the name of the TypeId group to display + */ void PrintGroup (std::string group) const; + /** Handler for \c \-\-PrintTypeIds: print all TypeId names. */ void PrintTypeIds (void) const; + /** Handler for \c \-\-PrintGroups: print all TypeId group names */ void PrintGroups (void) const; + /** + * Copy constructor + * + * \param cmd CommandLine to copy + */ void Copy (const CommandLine &cmd); + /** Remove all arguments, Usage(), name */ void Clear (void); - typedef std::list Items; - Items m_items; -}; + typedef std::list Items; /**< Argument list container */ + Items m_items; /**< The list of arguments */ + std::string m_usage; /**< The Usage string */ + std::string m_name; /**< The program name */ +}; // class CommandLine +/** + * \ingroup commandline + * Helpers for CommandLine + */ +namespace CommandLineHelper { + + /** + * \ingroup commandline + * \brief Helper to specialize UserItem on bool + * + * \param value the argument name + * \param val the argument location + * \return true if parsing was successful + * @{ + */ + template + bool UserItemParse (const std::string value, T & val); + template <> + bool UserItemParse (const std::string value, bool & val); + /**@}*/ + +} // namespace CommandLineHelper + + + } // namespace ns3 namespace ns3 { @@ -143,12 +333,19 @@ CommandLine::AddValue (const std::string &name, } template -bool +bool CommandLine::UserItem::Parse (std::string value) +{ + return CommandLineHelper::UserItemParse (value, *m_valuePtr); +} + +template +bool +CommandLineHelper::UserItemParse (const std::string value, T & val) { std::istringstream iss; iss.str (value); - iss >> (*m_valuePtr); + iss >> val; return !iss.bad () && !iss.fail (); } diff --git a/src/core/test/command-line-test-suite.cc b/src/core/test/command-line-test-suite.cc index 0114ebb1d..971eaf4d9 100644 --- a/src/core/test/command-line-test-suite.cc +++ b/src/core/test/command-line-test-suite.cc @@ -29,16 +29,28 @@ using namespace ns3; -// =========================================================================== -// A test base class that drives Command Line parsing -// =========================================================================== +/*************************************************************************//** + * A test base class that drives Command Line parsing + ****************************************************************************/ class CommandLineTestCaseBase : public TestCase { public: + /** + * Constructor + * + * \param description purpose of this TestCase + */ CommandLineTestCaseBase (std::string description); + /** Destructor */ virtual ~CommandLineTestCaseBase () {} - void Parse (const CommandLine &cmd, int n, ...); + /** + * Excercise the CommandLine with the provided arguments + * + * \param cmd the configured CommandLine + * \param n the number of arguments + */ + void Parse (CommandLine &cmd, int n, ...); }; CommandLineTestCaseBase::CommandLineTestCaseBase (std::string description) @@ -47,7 +59,7 @@ CommandLineTestCaseBase::CommandLineTestCaseBase (std::string description) } void -CommandLineTestCaseBase::Parse (const CommandLine &cmd, int n, ...) +CommandLineTestCaseBase::Parse (CommandLine &cmd, int n, ...) { char **args = new char* [n+1]; args[0] = (char *) "Test"; @@ -66,17 +78,17 @@ CommandLineTestCaseBase::Parse (const CommandLine &cmd, int n, ...) delete [] args; } -// =========================================================================== -// Test boolean Command Line processing -// =========================================================================== +/*************************************************************************//** + * Test boolean Command Line processing + ****************************************************************************/ class CommandLineBooleanTestCase : public CommandLineTestCaseBase { public: - CommandLineBooleanTestCase (); - virtual ~CommandLineBooleanTestCase () {} + CommandLineBooleanTestCase (); /**< Constructor */ + virtual ~CommandLineBooleanTestCase () {} /**< Destructor */ private: - virtual void DoRun (void); + virtual void DoRun (void); /**< Run the test */ }; @@ -97,20 +109,29 @@ CommandLineBooleanTestCase::DoRun (void) NS_TEST_ASSERT_MSG_EQ (myBool, false, "Command parser did not correctly set a boolean value to false"); Parse (cmd, 1, "--my-bool=1"); - NS_TEST_ASSERT_MSG_EQ (myBool, true, "Command parser did not correctly set a boolean value to true"); + NS_TEST_ASSERT_MSG_EQ (myBool, true, "Command parser did not correctly set a boolean value to true, given integer argument"); + + Parse (cmd, 1, "--my-bool"); + NS_TEST_ASSERT_MSG_EQ (myBool, true, "Command parser did not correctly set a boolean value to true, given no argument"); + + Parse (cmd, 1, "--my-bool=t"); + NS_TEST_ASSERT_MSG_EQ (myBool, true, "Command parser did not correctly set a boolean value to true, given 't' argument"); + + Parse (cmd, 1, "--my-bool=true"); + NS_TEST_ASSERT_MSG_EQ (myBool, true, "Command parser did not correctly set a boolean value to true, given \"true\" argument"); } -// =========================================================================== -// Test int Command Line processing -// =========================================================================== +/*************************************************************************//** + * Test int Command Line processing + ****************************************************************************/ class CommandLineIntTestCase : public CommandLineTestCaseBase { public: - CommandLineIntTestCase (); - virtual ~CommandLineIntTestCase () {} - -private: - virtual void DoRun (void); + CommandLineIntTestCase (); /**< Constructor */ + virtual ~CommandLineIntTestCase () {} /**< Destructor */ + +private: + virtual void DoRun (void); /**< Run the test */ }; @@ -138,17 +159,17 @@ CommandLineIntTestCase::DoRun (void) NS_TEST_ASSERT_MSG_EQ (myInt32, +2, "Command parser did not correctly set an integer value to +2"); } -// =========================================================================== -// Test unsigned int Command Line processing -// =========================================================================== +/*************************************************************************//** + * Test unsigned int Command Line processing + ****************************************************************************/ class CommandLineUnsignedIntTestCase : public CommandLineTestCaseBase { public: - CommandLineUnsignedIntTestCase (); - virtual ~CommandLineUnsignedIntTestCase () {} - -private: - virtual void DoRun (void); + CommandLineUnsignedIntTestCase (); /**< Constructor */ + virtual ~CommandLineUnsignedIntTestCase () {} /**< Destructor */ + +private: + virtual void DoRun (void); /**< Run the test */ }; @@ -173,17 +194,17 @@ CommandLineUnsignedIntTestCase::DoRun (void) NS_TEST_ASSERT_MSG_EQ (myUint32, 9, "Command parser did not correctly set an unsigned integer value to 9"); } -// =========================================================================== -// Test string Command Line processing -// =========================================================================== +/*************************************************************************//** + * Test string Command Line processing + ****************************************************************************/ class CommandLineStringTestCase : public CommandLineTestCaseBase { public: - CommandLineStringTestCase (); - virtual ~CommandLineStringTestCase () {} - -private: - virtual void DoRun (void); + CommandLineStringTestCase (); /**< Constructor */ + virtual ~CommandLineStringTestCase () {} /**< Destructor */ + +private: + virtual void DoRun (void); /**< Run the test */ }; @@ -208,13 +229,13 @@ CommandLineStringTestCase::DoRun (void) NS_TEST_ASSERT_MSG_EQ (myStr, "XX", "Command parser did not correctly set an string value to \"XX\""); } -// =========================================================================== -// The Test Suite that glues all of the Test Cases together. -// =========================================================================== +/*************************************************************************//** + * The Test Suite that glues all of the Test Cases together. + ****************************************************************************/ class CommandLineTestSuite : public TestSuite { public: - CommandLineTestSuite (); + CommandLineTestSuite (); /**< Constructor */ }; CommandLineTestSuite::CommandLineTestSuite () @@ -226,4 +247,4 @@ CommandLineTestSuite::CommandLineTestSuite () AddTestCase (new CommandLineStringTestCase, TestCase::QUICK); } -static CommandLineTestSuite CommandLineTestSuite; +static CommandLineTestSuite CommandLineTestSuite; /**< Test instance */