diff --git a/bindings/python/callbacks_list.py b/bindings/python/callbacks_list.py index db7a40c2c..17adacf99 100644 --- a/bindings/python/callbacks_list.py +++ b/bindings/python/callbacks_list.py @@ -5,6 +5,7 @@ callback_classes = [ ['void', 'ns3::Ptr', 'unsigned int', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'], ['void', 'ns3::Ptr', 'ns3::Address const&', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'], ['bool', 'ns3::Ptr', 'ns3::Address const&', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'], + ['bool', 'std::string', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty', 'ns3::empty'], ['bool', 'ns3::Ptr', 'ns3::Ptr', 'unsigned short', 'ns3::Address const&', 'ns3::Address const&', 'ns3::NetDevice::PacketType'], ['bool', 'ns3::Ptr', 'ns3::Ptr', 'unsigned short', 'ns3::Address const&', 'ns3::empty', 'ns3::empty'], ['void', 'ns3::Ptr', 'ns3::Ptr', 'unsigned short', 'ns3::Address const&', 'ns3::Address const&', 'ns3::NetDevice::PacketType'], diff --git a/bindings/python/ns3_module_core.py b/bindings/python/ns3_module_core.py index 8943021fe..1f8470d43 100644 --- a/bindings/python/ns3_module_core.py +++ b/bindings/python/ns3_module_core.py @@ -12,7 +12,7 @@ def register_types(module): ## callback.h: ns3::CallbackImplBase [class] module.add_class('CallbackImplBase', allow_subclassing=True, memory_policy=cppclass.ReferenceCountingMethodsPolicy(incref_method='Ref', decref_method='Unref', peekref_method='GetReferenceCount')) ## command-line.h: ns3::CommandLine [class] - module.add_class('CommandLine') + module.add_class('CommandLine', allow_subclassing=True) ## system-mutex.h: ns3::CriticalSection [class] module.add_class('CriticalSection') ## global-value.h: ns3::GlobalValue [class] @@ -350,6 +350,10 @@ def register_Ns3CommandLine_methods(root_module, cls): cls.add_constructor([param('ns3::CommandLine const &', 'arg0')]) ## command-line.h: ns3::CommandLine::CommandLine() [constructor] cls.add_constructor([]) + ## command-line.h: void ns3::CommandLine::AddValue(std::string const & name, std::string const & help, ns3::Callback callback) [member function] + cls.add_method('AddValue', + 'void', + [param('std::string const &', 'name'), param('std::string const &', 'help'), param('ns3::Callback< bool, std::string, ns3::empty, ns3::empty, ns3::empty, ns3::empty, ns3::empty >', 'callback')]) return def register_Ns3CriticalSection_methods(root_module, cls): diff --git a/bindings/python/ns3module_helpers.cc b/bindings/python/ns3module_helpers.cc index 03dc52445..b2a15ca49 100644 --- a/bindings/python/ns3module_helpers.cc +++ b/bindings/python/ns3module_helpers.cc @@ -1,3 +1,4 @@ +#include "ns3/ref-count-base.h" #include "ns3module.h" @@ -220,3 +221,61 @@ _wrap_TypeId_LookupByNameFailSafe(PyNs3TypeId *PYBINDGEN_UNUSED(dummy), PyObject return (PyObject *) py_tid; } + + +class CommandLinePythonValueSetter : public ns3::RefCountBase +{ + PyObject *m_namespace; + std::string m_variable; +public: + CommandLinePythonValueSetter (PyObject *ns, std::string const &variable) { + Py_INCREF(ns); + m_namespace = ns; + m_variable = variable; + } + bool Parse (std::string value) { + PyObject *pyvalue = PyString_FromStringAndSize (value.data(), value.size()); + PyObject_SetAttrString (m_namespace, m_variable.c_str(), pyvalue); + if (PyErr_Occurred()) { + PyErr_Print(); + return false; + } + return true; + } + virtual ~CommandLinePythonValueSetter () { + Py_DECREF (m_namespace); + m_namespace = NULL; + } + +}; + +PyObject * +_wrap_CommandLine_AddValue(PyNs3CommandLine *self, PyObject *args, PyObject *kwargs, + PyObject **return_exception) +{ + const char *name, *help, *variable = NULL; + PyObject *py_namespace = NULL; + const char *keywords[] = {"name", "help", "variable", "namespace", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "ss|sO", (char **) keywords, &name, &help, &variable, &py_namespace)) { + PyObject *exc_type, *traceback; + PyErr_Fetch(&exc_type, return_exception, &traceback); + Py_XDECREF(exc_type); + Py_XDECREF(traceback); + return NULL; + } + + if (variable == NULL) { + variable = name; + } + if (py_namespace == NULL) { + py_namespace = (PyObject *) self; + } + + ns3::Ptr setter = ns3::Create (py_namespace, variable); + self->obj->AddValue (name, help, ns3::MakeCallback (&CommandLinePythonValueSetter::Parse, setter)); + + Py_INCREF(Py_None); + return Py_None; +} + diff --git a/bindings/python/ns3modulegen_core_customizations.py b/bindings/python/ns3modulegen_core_customizations.py index abbf5ba80..f984617c5 100644 --- a/bindings/python/ns3modulegen_core_customizations.py +++ b/bindings/python/ns3modulegen_core_customizations.py @@ -292,6 +292,8 @@ def CommandLine_customizations(module): CommandLine = module['ns3::CommandLine'] CommandLine.add_method('Parse', None, [ArgvParam(None, 'argv')], is_static=False) + CommandLine.add_custom_method_wrapper("AddValue", "_wrap_CommandLine_AddValue", + flags=["METH_VARARGS", "METH_KEYWORDS"]) def Object_customizations(module): @@ -523,5 +525,5 @@ def TypeId_customizations(module): TypeId = module['ns3::TypeId'] TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe", flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"]) - + diff --git a/bindings/python/ns3modulescan.py b/bindings/python/ns3modulescan.py index 4b0684879..9e1eba6cc 100644 --- a/bindings/python/ns3modulescan.py +++ b/bindings/python/ns3modulescan.py @@ -56,6 +56,11 @@ type_annotations = { 'automatic_type_narrowing': 'true', 'allow_subclassing': 'false', }, + + '::ns3::CommandLine': { + 'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object + }, + 'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': { 'ignore': None, }, diff --git a/bindings/python/wscript b/bindings/python/wscript index d255bd605..8c2f6ca13 100644 --- a/bindings/python/wscript +++ b/bindings/python/wscript @@ -309,6 +309,7 @@ __dummy_function_to_force_template_instantiation_v2 () t1 > t2; } + } """ outfile.close() diff --git a/src/core/command-line.cc b/src/core/command-line.cc index 63fc18657..ecbc77016 100644 --- a/src/core/command-line.cc +++ b/src/core/command-line.cc @@ -253,6 +253,27 @@ CommandLine::HandleArgument (std::string name, std::string value) const } } +bool +CommandLine::CallbackItem::Parse (std::string value) +{ + NS_LOG_DEBUG ("CommandLine::CallbackItem::Parse \"" << value << "\""); + return m_callback (value); +} + +void +CommandLine::AddValue (const std::string &name, + const std::string &help, + Callback callback) +{ + NS_LOG_FUNCTION (this << name << help << "callback"); + CallbackItem *item = new CallbackItem (); + item->m_name = name; + item->m_help = help; + item->m_callback = callback; + m_items.push_back (item); +} + + } // namespace ns3 #ifdef RUN_SELF_TESTS diff --git a/src/core/command-line.h b/src/core/command-line.h index a03cff92b..9b40082a1 100644 --- a/src/core/command-line.h +++ b/src/core/command-line.h @@ -24,6 +24,8 @@ #include #include +#include "ns3/callback.h" + namespace ns3 { /** @@ -56,6 +58,17 @@ public: const std::string &help, T &value); + + /** + * \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. + */ + void AddValue (const std::string &name, + const std::string &help, + Callback callback); + /** * \param argc the 'argc' variable: number of arguments (including the * main program name as first element). @@ -82,6 +95,13 @@ private: virtual bool Parse (std::string value); T *m_valuePtr; }; + class CallbackItem : public Item + { + public: + virtual bool Parse (std::string value); + Callback m_callback; + }; + void HandleArgument (std::string name, std::string value) const; void PrintHelp (void) const; void PrintGlobals (void) const; diff --git a/utils/python-unit-tests.py b/utils/python-unit-tests.py index 4ea16dd2d..00713e8d9 100644 --- a/utils/python-unit-tests.py +++ b/utils/python-unit-tests.py @@ -123,5 +123,26 @@ class TestSimulator(unittest.TestCase): self.assertRaises(KeyError, ns3.TypeId.LookupByNameFailSafe, "__InvalidTypeName__") + def testCommandLine(self): + cmd = ns3.CommandLine() + cmd.AddValue("Test1", "this is a test option") + cmd.AddValue("Test2", "this is a test option") + cmd.AddValue("Test3", "this is a test option", variable="test_xxx") + cmd.Test1 = None + cmd.Test2 = None + cmd.test_xxx = None + class Foo: + pass + foo = Foo() + foo.test_foo = None + cmd.AddValue("Test4", "this is a test option", variable="test_foo", namespace=foo) + + cmd.Parse(["python", "--Test1=value1", "--Test2=value2", "--Test3=123", "--Test4=xpto"]) + + self.assertEqual(cmd.Test1, "value1") + self.assertEqual(cmd.Test2, "value2") + self.assertEqual(cmd.test_xxx, "123") + self.assertEqual(foo.test_foo, "xpto") + if __name__ == '__main__': unittest.main()