--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/bindings/modulegen_customizations.py Sat Mar 12 18:34:30 2011 +0000
@@ -0,0 +1,456 @@
+import re
+
+from pybindgen.typehandlers import base as typehandlers
+from pybindgen import ReturnValue, Parameter
+from pybindgen.cppmethod import CustomCppMethodWrapper, CustomCppConstructorWrapper
+from pybindgen.typehandlers.codesink import MemoryCodeSink
+from pybindgen.typehandlers import ctypeparser
+from pybindgen import cppclass
+import warnings
+
+from pybindgen.typehandlers.base import CodeGenerationError
+
+import sys
+
+class SmartPointerTransformation(typehandlers.TypeTransformation):
+ """
+ This class provides a "type transformation" that tends to support
+ NS-3 smart pointers. Parameters such as "Ptr<Foo> foo" are
+ transformed into something like Parameter.new("Foo*", "foo",
+ transfer_ownership=False). Return values such as Ptr<Foo> are
+ transformed into ReturnValue.new("Foo*",
+ caller_owns_return=False). Since the underlying objects have
+ reference counting, PyBindGen does the right thing.
+ """
+ def __init__(self):
+ super(SmartPointerTransformation, self).__init__()
+ self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>\s*$')
+
+ def _get_untransformed_type_traits(self, name):
+ m = self.rx.match(name)
+ is_const = False
+ if m is None:
+ return None, False
+ else:
+ name1 = m.group(2).strip()
+ if name1.startswith('const '):
+ name1 = name1[len('const '):]
+ is_const = True
+ if name1.endswith(' const'):
+ name1 = name1[:-len(' const')]
+ is_const = True
+ new_name = name1+' *'
+
+ if new_name.startswith('::'):
+ new_name = new_name[2:]
+ return new_name, is_const
+
+ def get_untransformed_name(self, name):
+ new_name, dummy_is_const = self._get_untransformed_type_traits(name)
+ return new_name
+
+ def create_type_handler(self, type_handler, *args, **kwargs):
+ if issubclass(type_handler, Parameter):
+ kwargs['transfer_ownership'] = False
+ elif issubclass(type_handler, ReturnValue):
+ kwargs['caller_owns_return'] = False
+ else:
+ raise AssertionError
+
+ ## fix the ctype, add ns3:: namespace
+ orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
+ if is_const:
+ correct_ctype = 'ns3::Ptr< %s const >' % orig_ctype[:-2]
+ else:
+ correct_ctype = 'ns3::Ptr< %s >' % orig_ctype[:-2]
+ args = tuple([correct_ctype] + list(args[1:]))
+
+ handler = type_handler(*args, **kwargs)
+ handler.set_tranformation(self, orig_ctype)
+ return handler
+
+ def untransform(self, type_handler, declarations, code_block, expression):
+ return 'const_cast<%s> (ns3::PeekPointer (%s))' % (type_handler.untransformed_ctype, expression)
+
+ def transform(self, type_handler, declarations, code_block, expression):
+ assert type_handler.untransformed_ctype[-1] == '*'
+ return 'ns3::Ptr< %s > (%s)' % (type_handler.untransformed_ctype[:-1], expression)
+
+## register the type transformation
+transf = SmartPointerTransformation()
+typehandlers.return_type_matcher.register_transformation(transf)
+typehandlers.param_type_matcher.register_transformation(transf)
+del transf
+
+
+class ArgvParam(Parameter):
+ """
+ Converts a python list-of-strings argument to a pair of 'int argc,
+ char *argv[]' arguments to pass into C.
+
+ One Python argument becomes two C function arguments -> it's a miracle!
+
+ Note: this parameter type handler is not registered by any name;
+ must be used explicitly.
+ """
+
+ DIRECTIONS = [Parameter.DIRECTION_IN]
+ CTYPES = []
+
+ def convert_c_to_python(self, wrapper):
+ raise NotImplementedError
+
+ def convert_python_to_c(self, wrapper):
+ py_name = wrapper.declarations.declare_variable('PyObject*', 'py_' + self.name)
+ argc_var = wrapper.declarations.declare_variable('int', 'argc')
+ name = wrapper.declarations.declare_variable('char**', self.name)
+ idx = wrapper.declarations.declare_variable('Py_ssize_t', 'idx')
+ wrapper.parse_params.add_parameter('O!', ['&PyList_Type', '&'+py_name], self.name)
+
+ #wrapper.before_call.write_error_check('!PyList_Check(%s)' % py_name) # XXX
+
+ wrapper.before_call.write_code("%s = (char **) malloc(sizeof(char*)*PyList_Size(%s));"
+ % (name, py_name))
+ wrapper.before_call.add_cleanup_code('free(%s);' % name)
+ wrapper.before_call.write_code('''
+for (%(idx)s = 0; %(idx)s < PyList_Size(%(py_name)s); %(idx)s++)
+{
+''' % vars())
+ wrapper.before_call.sink.indent()
+ wrapper.before_call.write_code('''
+PyObject *item = PyList_GET_ITEM(%(py_name)s, %(idx)s);
+''' % vars())
+ #wrapper.before_call.write_error_check('item == NULL')
+ wrapper.before_call.write_error_check(
+ '!PyString_Check(item)',
+ failure_cleanup=('PyErr_SetString(PyExc_TypeError, '
+ '"argument %s must be a list of strings");') % self.name)
+ wrapper.before_call.write_code(
+ '%s[%s] = PyString_AsString(item);' % (name, idx))
+ wrapper.before_call.sink.unindent()
+ wrapper.before_call.write_code('}')
+ wrapper.before_call.write_code('%s = PyList_Size(%s);' % (argc_var, py_name))
+
+ wrapper.call_params.append(argc_var)
+ wrapper.call_params.append(name)
+
+
+class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
+ """
+ Class that generates a proxy virtual method that calls a similarly named python method.
+ """
+
+ def __init__(self, return_value, parameters):
+ super(CallbackImplProxyMethod, self).__init__(return_value, parameters)
+
+ def generate_python_call(self):
+ """code to call the python method"""
+ build_params = self.build_params.get_parameters(force_tuple_creation=True)
+ if build_params[0][0] == '"':
+ build_params[0] = '(char *) ' + build_params[0]
+ args = self.before_call.declare_variable('PyObject*', 'args')
+ self.before_call.write_code('%s = Py_BuildValue(%s);'
+ % (args, ', '.join(build_params)))
+ self.before_call.add_cleanup_code('Py_DECREF(%s);' % args)
+ self.before_call.write_code('py_retval = PyObject_CallObject(m_callback, %s);' % args)
+ self.before_call.write_error_check('py_retval == NULL')
+ self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
+
+
+
+
+def generate_callback_classes(out, callbacks):
+ for callback_impl_num, template_parameters in enumerate(callbacks):
+ sink = MemoryCodeSink()
+ cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
+ #print >> sys.stderr, "***** trying to register callback: %r" % cls_name
+ class_name = "PythonCallbackImpl%i" % callback_impl_num
+ sink.writeln('''
+class %s : public ns3::CallbackImpl<%s>
+{
+public:
+ PyObject *m_callback;
+ %s(PyObject *callback)
+ {
+ Py_INCREF(callback);
+ m_callback = callback;
+ }
+ virtual ~%s()
+ {
+ Py_DECREF(m_callback);
+ m_callback = NULL;
+ }
+
+ virtual bool IsEqual(ns3::Ptr<const ns3::CallbackImplBase> other_base) const
+ {
+ const %s *other = dynamic_cast<const %s*> (ns3::PeekPointer (other_base));
+ if (other != NULL)
+ return (other->m_callback == m_callback);
+ else
+ return false;
+ }
+
+''' % (class_name, ', '.join(template_parameters), class_name, class_name, class_name, class_name))
+ sink.indent()
+ callback_return = template_parameters[0]
+ return_ctype = ctypeparser.parse_type(callback_return)
+ if ('const' in return_ctype.remove_modifiers()):
+ kwargs = {'is_const': True}
+ else:
+ kwargs = {}
+ try:
+ return_type = ReturnValue.new(str(return_ctype), **kwargs)
+ except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
+ warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
+ % (callback_return, cls_name, ex),
+ Warning)
+ continue
+
+ arguments = []
+ ok = True
+ callback_parameters = [arg for arg in template_parameters[1:] if arg != 'ns3::empty']
+ for arg_num, arg_type in enumerate(callback_parameters):
+ arg_name = 'arg%i' % (arg_num+1)
+
+ param_ctype = ctypeparser.parse_type(arg_type)
+ if ('const' in param_ctype.remove_modifiers()):
+ kwargs = {'is_const': True}
+ else:
+ kwargs = {}
+ try:
+ arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
+ except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
+ warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
+ % (arg_type, arg_name, cls_name, ex),
+ Warning)
+ ok = False
+ if not ok:
+ continue
+
+ wrapper = CallbackImplProxyMethod(return_type, arguments)
+ wrapper.generate(sink, 'operator()', decl_modifiers=[])
+
+ sink.unindent()
+ sink.writeln('};\n')
+ sink.flush_to(out)
+
+ class PythonCallbackParameter(Parameter):
+ "Class handlers"
+ CTYPES = [cls_name]
+ #print >> sys.stderr, "***** registering callback handler: %r" % ctypeparser.normalize_type_string(cls_name)
+ DIRECTIONS = [Parameter.DIRECTION_IN]
+ PYTHON_CALLBACK_IMPL_NAME = class_name
+ TEMPLATE_ARGS = template_parameters
+
+ def convert_python_to_c(self, wrapper):
+ "parses python args to get C++ value"
+ assert isinstance(wrapper, typehandlers.ForwardWrapperBase)
+
+ if self.default_value is None:
+ py_callback = wrapper.declarations.declare_variable('PyObject*', self.name)
+ wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name)
+ wrapper.before_call.write_error_check(
+ '!PyCallable_Check(%s)' % py_callback,
+ 'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
+ callback_impl = wrapper.declarations.declare_variable(
+ 'ns3::Ptr<%s>' % self.PYTHON_CALLBACK_IMPL_NAME,
+ '%s_cb_impl' % self.name)
+ wrapper.before_call.write_code("%s = ns3::Create<%s> (%s);"
+ % (callback_impl, self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
+ wrapper.call_params.append(
+ 'ns3::Callback<%s> (%s)' % (', '.join(self.TEMPLATE_ARGS), callback_impl))
+ else:
+ py_callback = wrapper.declarations.declare_variable('PyObject*', self.name, 'NULL')
+ wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name, optional=True)
+ value = wrapper.declarations.declare_variable(
+ 'ns3::Callback<%s>' % ', '.join(self.TEMPLATE_ARGS),
+ self.name+'_value',
+ self.default_value)
+
+ wrapper.before_call.write_code("if (%s) {" % (py_callback,))
+ wrapper.before_call.indent()
+
+ wrapper.before_call.write_error_check(
+ '!PyCallable_Check(%s)' % py_callback,
+ 'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
+
+ wrapper.before_call.write_code("%s = ns3::Callback<%s> (ns3::Create<%s> (%s));"
+ % (value, ', '.join(self.TEMPLATE_ARGS),
+ self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
+
+ wrapper.before_call.unindent()
+ wrapper.before_call.write_code("}") # closes: if (py_callback) {
+
+ wrapper.call_params.append(value)
+
+
+ def convert_c_to_python(self, wrapper):
+ raise typehandlers.NotSupportedError("Reverse wrappers for ns3::Callback<...> types "
+ "(python using callbacks defined in C++) not implemented.")
+
+
+# def write_preamble(out):
+# pybindgen.write_preamble(out)
+# out.writeln("#include \"ns3/everything.h\"")
+
+
+
+def Simulator_customizations(module):
+ Simulator = module['ns3::Simulator']
+
+ ## Simulator::Schedule(delay, callback, ...user..args...)
+ Simulator.add_custom_method_wrapper("Schedule", "_wrap_Simulator_Schedule",
+ flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
+
+
+ ## Simulator::ScheduleNow(callback, ...user..args...)
+ Simulator.add_custom_method_wrapper("ScheduleNow", "_wrap_Simulator_ScheduleNow",
+ flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
+
+
+ ## Simulator::ScheduleDestroy(callback, ...user..args...)
+ Simulator.add_custom_method_wrapper("ScheduleDestroy", "_wrap_Simulator_ScheduleDestroy",
+ flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
+
+ Simulator.add_custom_method_wrapper("Run", "_wrap_Simulator_Run",
+ flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
+
+
+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):
+# ## ---------------------------------------------------------------------
+# ## Here we generate custom constructor code for all classes that
+# ## derive from ns3::Object. The custom constructors are needed in
+# ## order to support kwargs only and to translate kwargs into ns3
+# ## attributes, etc.
+# ## ---------------------------------------------------------------------
+# Object = module['ns3::Object']
+
+
+# ## add a GetTypeId method to all generatd helper classes
+# def helper_class_hook(helper_class):
+# decl = """
+# static ns3::TypeId GetTypeId (void)
+# {
+# static ns3::TypeId tid = ns3::TypeId ("%s")
+# .SetParent< %s > ()
+# ;
+# return tid;
+# }""" % (helper_class.name, helper_class.class_.full_name)
+
+# helper_class.add_custom_method(decl)
+# helper_class.add_post_generation_code(
+# "NS_OBJECT_ENSURE_REGISTERED (%s);" % helper_class.name)
+# Object.add_helper_class_hook(helper_class_hook)
+
+# def ns3_object_instance_creation_function(cpp_class, code_block, lvalue,
+# parameters, construct_type_name):
+# assert lvalue
+# assert not lvalue.startswith('None')
+# if cpp_class.cannot_be_constructed:
+# raise CodeGenerationError("%s cannot be constructed (%s)"
+# % cpp_class.full_name)
+# if cpp_class.incomplete_type:
+# raise CodeGenerationError("%s cannot be constructed (incomplete type)"
+# % cpp_class.full_name)
+# code_block.write_code("%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
+# code_block.write_code("%s->Ref ();" % (lvalue))
+
+# def ns3_object_post_instance_creation_function(cpp_class, code_block, lvalue,
+# parameters, construct_type_name):
+# code_block.write_code("ns3::CompleteConstruct(%s);" % (lvalue, ))
+
+# Object.set_instance_creation_function(ns3_object_instance_creation_function)
+# Object.set_post_instance_creation_function(ns3_object_post_instance_creation_function)
+
+
+# def Attribute_customizations(module):
+# # Fix up for the "const AttributeValue &v = EmptyAttribute()"
+# # case, as used extensively by helper classes.
+
+# # Here's why we need to do this: pybindgen.gccxmlscanner, when
+# # scanning parameter default values, is only provided with the
+# # value as a simple C expression string. (py)gccxml does not
+# # report the type of the default value.
+
+# # As a workaround, here we iterate over all parameters of all
+# # methods of all classes and tell pybindgen what is the type of
+# # the default value for attributes.
+
+# for cls in module.classes:
+# for meth in cls.get_all_methods():
+# for param in meth.parameters:
+# if isinstance(param, cppclass.CppClassRefParameter):
+# if param.cpp_class.name == 'AttributeValue' \
+# and param.default_value is not None \
+# and param.default_value_type is None:
+# param.default_value_type = 'ns3::EmptyAttributeValue'
+
+
+def TypeId_customizations(module):
+ TypeId = module['ns3::TypeId']
+ TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",
+ flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
+
+
+def add_std_ofstream(module):
+ module.add_include('<fstream>')
+ ostream = module.add_class('ostream', foreign_cpp_namespace='::std')
+ ostream.set_cannot_be_constructed("abstract base class")
+ ofstream = module.add_class('ofstream', foreign_cpp_namespace='::std', parent=ostream)
+ ofstream.add_enum('openmode', [
+ ('app', 'std::ios_base::app'),
+ ('ate', 'std::ios_base::ate'),
+ ('binary', 'std::ios_base::binary'),
+ ('in', 'std::ios_base::in'),
+ ('out', 'std::ios_base::out'),
+ ('trunc', 'std::ios_base::trunc'),
+ ])
+ ofstream.add_constructor([Parameter.new("const char *", 'filename'),
+ Parameter.new("::std::ofstream::openmode", 'mode', default_value="std::ios_base::out")])
+ ofstream.add_method('close', None, [])
+
+ import pybindgen.typehandlers.base
+ for alias in "std::_Ios_Openmode", "std::ios::openmode":
+ pybindgen.typehandlers.base.param_type_matcher.add_type_alias(alias, "int")
+
+ for flag in 'in', 'out', 'ate', 'app', 'trunc', 'binary':
+ module.after_init.write_code('PyModule_AddIntConstant(m, (char *) "STD_IOS_%s", std::ios::%s);'
+ % (flag.upper(), flag))
+
+
+
+def add_ipv4_address_tp_hash(module):
+ module.body.writeln('''
+long
+_ns3_Ipv4Address_tp_hash (PyObject *obj)
+{
+ PyNs3Ipv4Address *addr = reinterpret_cast<PyNs3Ipv4Address *> (obj);
+ return static_cast<long> (ns3::Ipv4AddressHash () (*addr->obj));
+}
+''')
+ module.header.writeln('long _ns3_Ipv4Address_tp_hash (PyObject *obj);')
+ module['Ipv4Address'].pytype.slots['tp_hash'] = "_ns3_Ipv4Address_tp_hash"
+
+
+
+def post_register_types(root_module):
+ Simulator_customizations(root_module)
+ CommandLine_customizations(root_module)
+ TypeId_customizations(root_module)
+ add_std_ofstream(root_module)
+
+def post_register_functions(root_module):
+ # these are already in the main script, so commented out here
+ #Object_customizations(root_module)
+ #Attribute_customizations(root_module)
+ pass
+
+