bindings/python/ns3modulegen_core_customizations.py
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 04 Jul 2009 08:15:48 +0200
changeset 4654 2eaebe77d66b
parent 4196 ed59d07c5373
permissions -rw-r--r--
Added tag ns-3.5 for changeset c975274c9707
gjc@3408
     1
import re
gjc@3408
     2
gjc@3408
     3
from pybindgen.typehandlers import base as typehandlers
gjc@3473
     4
from pybindgen import ReturnValue, Parameter
gjc@3408
     5
from pybindgen.cppmethod import CustomCppMethodWrapper, CustomCppConstructorWrapper
gjc@3408
     6
from pybindgen.typehandlers.codesink import MemoryCodeSink
gjc@3408
     7
from pybindgen.typehandlers import ctypeparser
gjc@3421
     8
from pybindgen import cppclass
gjc@3408
     9
import warnings
gjc@3408
    10
gjc@3408
    11
from pybindgen.typehandlers.base import CodeGenerationError
gjc@3408
    12
gjc@3408
    13
import sys
gjc@3408
    14
gjc@3408
    15
class SmartPointerTransformation(typehandlers.TypeTransformation):
gjc@3408
    16
    """
gjc@3408
    17
    This class provides a "type transformation" that tends to support
gjc@3408
    18
    NS-3 smart pointers.  Parameters such as "Ptr<Foo> foo" are
gjc@3408
    19
    transformed into something like Parameter.new("Foo*", "foo",
gjc@3408
    20
    transfer_ownership=False).  Return values such as Ptr<Foo> are
gjc@3408
    21
    transformed into ReturnValue.new("Foo*",
gjc@3408
    22
    caller_owns_return=False).  Since the underlying objects have
gjc@3408
    23
    reference counting, PyBindGen does the right thing.
gjc@3408
    24
    """
gjc@3408
    25
    def __init__(self):
gjc@3408
    26
        super(SmartPointerTransformation, self).__init__()
gjc@3408
    27
        self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>')
gjc@3408
    28
gjc@3408
    29
    def _get_untransformed_type_traits(self, name):
gjc@3408
    30
        m = self.rx.match(name)
gjc@3408
    31
        is_const = False
gjc@3408
    32
        if m is None:
gjc@3408
    33
            return None, False
gjc@3408
    34
        else:
gjc@3408
    35
            name1 = m.group(2).strip()
gjc@3408
    36
            if name1.startswith('const '):
gjc@3408
    37
                name1 = name1[len('const '):]
gjc@3408
    38
                is_const = True
gjc@3408
    39
            if name1.endswith(' const'):
gjc@3408
    40
                name1 = name1[:-len(' const')]
gjc@3408
    41
                is_const = True
gjc@3408
    42
            new_name = name1+' *'
gjc@3408
    43
gjc@3408
    44
            if new_name.startswith('::'):
gjc@3408
    45
                new_name = new_name[2:]
gjc@3408
    46
            return new_name, is_const
gjc@3408
    47
gjc@3408
    48
    def get_untransformed_name(self, name):
gjc@3408
    49
        new_name, dummy_is_const = self._get_untransformed_type_traits(name)
gjc@3408
    50
        return new_name
gjc@3408
    51
gjc@3408
    52
    def create_type_handler(self, type_handler, *args, **kwargs):
gjc@3408
    53
        if issubclass(type_handler, Parameter):
gjc@3408
    54
            kwargs['transfer_ownership'] = False
gjc@3408
    55
        elif issubclass(type_handler, ReturnValue):
gjc@3408
    56
            kwargs['caller_owns_return'] = False
gjc@3408
    57
        else:
gjc@3408
    58
            raise AssertionError
gjc@3408
    59
gjc@3408
    60
        ## fix the ctype, add ns3:: namespace
gjc@3408
    61
        orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
gjc@3408
    62
        if is_const:
gjc@3408
    63
            correct_ctype = 'ns3::Ptr< %s const >' % orig_ctype[:-2]
gjc@3408
    64
        else:
gjc@3408
    65
            correct_ctype = 'ns3::Ptr< %s >' % orig_ctype[:-2]
gjc@3408
    66
        args = tuple([correct_ctype] + list(args[1:]))
gjc@3408
    67
gjc@3408
    68
        handler = type_handler(*args, **kwargs)
gjc@3408
    69
        handler.set_tranformation(self, orig_ctype)
gjc@3408
    70
        return handler
gjc@3408
    71
gjc@3408
    72
    def untransform(self, type_handler, declarations, code_block, expression):
gjc@3574
    73
        return 'const_cast<%s> (ns3::PeekPointer (%s))' % (type_handler.untransformed_ctype, expression)
gjc@3408
    74
gjc@3408
    75
    def transform(self, type_handler, declarations, code_block, expression):
gjc@3408
    76
        assert type_handler.untransformed_ctype[-1] == '*'
gjc@3408
    77
        return 'ns3::Ptr< %s > (%s)' % (type_handler.untransformed_ctype[:-1], expression)
gjc@3408
    78
gjc@3408
    79
## register the type transformation
gjc@3408
    80
transf = SmartPointerTransformation()
gjc@3408
    81
typehandlers.return_type_matcher.register_transformation(transf)
gjc@3408
    82
typehandlers.param_type_matcher.register_transformation(transf)
gjc@3408
    83
del transf
gjc@3408
    84
gjc@3408
    85
gjc@3408
    86
class ArgvParam(Parameter):
gjc@3408
    87
    """
gjc@3408
    88
    Converts a python list-of-strings argument to a pair of 'int argc,
gjc@3408
    89
    char *argv[]' arguments to pass into C.
gjc@3408
    90
gjc@3408
    91
    One Python argument becomes two C function arguments -> it's a miracle!
gjc@3408
    92
gjc@3408
    93
    Note: this parameter type handler is not registered by any name;
gjc@3408
    94
    must be used explicitly.
gjc@3408
    95
    """
gjc@3408
    96
gjc@3408
    97
    DIRECTIONS = [Parameter.DIRECTION_IN]
gjc@3408
    98
    CTYPES = []
gjc@3408
    99
    
gjc@3408
   100
    def convert_c_to_python(self, wrapper):
gjc@3408
   101
        raise NotImplementedError
gjc@3408
   102
gjc@3408
   103
    def convert_python_to_c(self, wrapper):
gjc@3408
   104
        py_name = wrapper.declarations.declare_variable('PyObject*', 'py_' + self.name)
gjc@3408
   105
        argc_var = wrapper.declarations.declare_variable('int', 'argc')
gjc@3408
   106
        name = wrapper.declarations.declare_variable('char**', self.name)
gjc@3408
   107
        idx = wrapper.declarations.declare_variable('Py_ssize_t', 'idx')
gjc@3408
   108
        wrapper.parse_params.add_parameter('O!', ['&PyList_Type', '&'+py_name], self.name)
gjc@3408
   109
gjc@3408
   110
        #wrapper.before_call.write_error_check('!PyList_Check(%s)' % py_name) # XXX
gjc@3408
   111
gjc@3408
   112
        wrapper.before_call.write_code("%s = (char **) malloc(sizeof(char*)*PyList_Size(%s));"
gjc@3408
   113
                                       % (name, py_name))
gjc@3408
   114
        wrapper.before_call.add_cleanup_code('free(%s);' % name)
gjc@3408
   115
        wrapper.before_call.write_code('''
gjc@3408
   116
for (%(idx)s = 0; %(idx)s < PyList_Size(%(py_name)s); %(idx)s++)
gjc@3408
   117
{
gjc@3408
   118
''' % vars())
gjc@3408
   119
        wrapper.before_call.sink.indent()
gjc@3408
   120
        wrapper.before_call.write_code('''
gjc@3408
   121
PyObject *item = PyList_GET_ITEM(%(py_name)s, %(idx)s);
gjc@3408
   122
''' % vars())
gjc@3408
   123
        #wrapper.before_call.write_error_check('item == NULL')
gjc@3408
   124
        wrapper.before_call.write_error_check(
gjc@3408
   125
            '!PyString_Check(item)',
gjc@3408
   126
            failure_cleanup=('PyErr_SetString(PyExc_TypeError, '
gjc@3408
   127
                             '"argument %s must be a list of strings");') % self.name)
gjc@3408
   128
        wrapper.before_call.write_code(
gjc@3408
   129
            '%s[%s] = PyString_AsString(item);' % (name, idx))
gjc@3408
   130
        wrapper.before_call.sink.unindent()
gjc@3408
   131
        wrapper.before_call.write_code('}')
gjc@3408
   132
        wrapper.before_call.write_code('%s = PyList_Size(%s);' % (argc_var, py_name))
gjc@3408
   133
        
gjc@3408
   134
        wrapper.call_params.append(argc_var)
gjc@3408
   135
        wrapper.call_params.append(name)
gjc@3408
   136
gjc@3408
   137
gjc@3408
   138
class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
gjc@3408
   139
    """
gjc@3408
   140
    Class that generates a proxy virtual method that calls a similarly named python method.
gjc@3408
   141
    """
gjc@3408
   142
gjc@3408
   143
    def __init__(self, return_value, parameters):
gjc@3408
   144
        super(CallbackImplProxyMethod, self).__init__(return_value, parameters)
gjc@3408
   145
gjc@3408
   146
    def generate_python_call(self):
gjc@3408
   147
        """code to call the python method"""
gjc@3412
   148
        build_params = self.build_params.get_parameters(force_tuple_creation=True)
gjc@3408
   149
        if build_params[0][0] == '"':
gjc@3408
   150
            build_params[0] = '(char *) ' + build_params[0]
gjc@3408
   151
        args = self.before_call.declare_variable('PyObject*', 'args')
gjc@3408
   152
        self.before_call.write_code('%s = Py_BuildValue(%s);'
gjc@3408
   153
                                    % (args, ', '.join(build_params)))
gjc@3408
   154
        self.before_call.add_cleanup_code('Py_DECREF(%s);' % args)
gjc@3408
   155
        self.before_call.write_code('py_retval = PyObject_CallObject(m_callback, %s);' % args)
gjc@3408
   156
        self.before_call.write_error_check('py_retval == NULL')
gjc@3408
   157
        self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
gjc@3408
   158
gjc@3408
   159
gjc@3408
   160
gjc@3408
   161
gjc@3408
   162
def generate_callback_classes(out, callbacks):
gjc@3408
   163
    for callback_impl_num, template_parameters in enumerate(callbacks):
gjc@3408
   164
        sink = MemoryCodeSink()
gjc@3408
   165
        cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
gjc@3408
   166
        #print >> sys.stderr, "***** trying to register callback: %r" % cls_name
gjc@3408
   167
        class_name = "PythonCallbackImpl%i" % callback_impl_num
gjc@3408
   168
        sink.writeln('''
gjc@3408
   169
class %s : public ns3::CallbackImpl<%s>
gjc@3408
   170
{
gjc@3408
   171
public:
gjc@3408
   172
    PyObject *m_callback;
gjc@3408
   173
    %s(PyObject *callback)
gjc@3408
   174
    {
gjc@3408
   175
        Py_INCREF(callback);
gjc@3408
   176
        m_callback = callback;
gjc@3408
   177
    }
gjc@3408
   178
    virtual ~%s()
gjc@3408
   179
    {
gjc@3408
   180
        Py_DECREF(m_callback);
gjc@3408
   181
        m_callback = NULL;
gjc@3408
   182
    }
gjc@3408
   183
gjc@3408
   184
    virtual bool IsEqual(ns3::Ptr<const ns3::CallbackImplBase> other_base) const
gjc@3408
   185
    {
gjc@3408
   186
        const %s *other = dynamic_cast<const %s*> (ns3::PeekPointer (other_base));
gjc@3408
   187
        if (other != NULL)
gjc@3408
   188
            return (other->m_callback == m_callback);
gjc@3408
   189
        else
gjc@3408
   190
            return false;
gjc@3408
   191
    }
gjc@3408
   192
gjc@3408
   193
''' % (class_name, ', '.join(template_parameters), class_name, class_name, class_name, class_name))
gjc@3408
   194
        sink.indent()
gjc@3408
   195
        callback_return = template_parameters[0]
gjc@3408
   196
        return_ctype = ctypeparser.parse_type(callback_return)
gjc@3408
   197
        if ('const' in return_ctype.remove_modifiers()):
gjc@3408
   198
            kwargs = {'is_const': True}
gjc@3408
   199
        else:
gjc@3408
   200
            kwargs = {}
gjc@3408
   201
        try:
gjc@3408
   202
            return_type = ReturnValue.new(str(return_ctype), **kwargs)
gjc@3408
   203
        except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
gjc@3408
   204
            warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
gjc@3408
   205
                          % (callback_return, cls_name, ex),
gjc@3408
   206
                          Warning)
gjc@3408
   207
            continue
gjc@3408
   208
gjc@3408
   209
        arguments = []
gjc@3408
   210
        ok = True
gjc@3408
   211
        callback_parameters = [arg for arg in template_parameters[1:] if arg != 'ns3::empty']
gjc@3408
   212
        for arg_num, arg_type in enumerate(callback_parameters):
gjc@3408
   213
            arg_name = 'arg%i' % (arg_num+1)
gjc@3408
   214
gjc@3408
   215
            param_ctype = ctypeparser.parse_type(arg_type)
gjc@3408
   216
            if ('const' in param_ctype.remove_modifiers()):
gjc@3408
   217
                kwargs = {'is_const': True}
gjc@3408
   218
            else:
gjc@3408
   219
                kwargs = {}
gjc@3408
   220
            try:
gjc@3408
   221
                arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
gjc@3408
   222
            except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError), ex:
gjc@3408
   223
                warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
gjc@3408
   224
                              % (arg_type, arg_name, cls_name, ex),
gjc@3408
   225
                              Warning)
gjc@3408
   226
                ok = False
gjc@3408
   227
        if not ok:
gjc@3408
   228
            continue
gjc@3408
   229
gjc@3408
   230
        wrapper = CallbackImplProxyMethod(return_type, arguments)
gjc@3408
   231
        wrapper.generate(sink, 'operator()', decl_modifiers=[])
gjc@3408
   232
            
gjc@3408
   233
        sink.unindent()
gjc@3408
   234
        sink.writeln('};\n')
gjc@3408
   235
        sink.flush_to(out)
gjc@3408
   236
        
gjc@3408
   237
        class PythonCallbackParameter(Parameter):
gjc@3408
   238
            "Class handlers"
gjc@3408
   239
            CTYPES = [cls_name]
gjc@3408
   240
            #print >> sys.stderr, "***** registering callback handler: %r" % ctypeparser.normalize_type_string(cls_name)
gjc@3408
   241
            DIRECTIONS = [Parameter.DIRECTION_IN]
gjc@3408
   242
            PYTHON_CALLBACK_IMPL_NAME = class_name
gjc@3408
   243
            TEMPLATE_ARGS = template_parameters
gjc@3408
   244
gjc@3408
   245
            def convert_python_to_c(self, wrapper):
gjc@3408
   246
                "parses python args to get C++ value"
gjc@3408
   247
                assert isinstance(wrapper, typehandlers.ForwardWrapperBase)
gjc@3408
   248
gjc@3408
   249
                py_callback = wrapper.declarations.declare_variable('PyObject*', self.name)
gjc@3408
   250
                wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name)
gjc@3408
   251
                wrapper.before_call.write_error_check(
gjc@3408
   252
                    '!PyCallable_Check(%s)' % py_callback,
gjc@3408
   253
                    'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
gjc@3408
   254
                callback_impl = wrapper.declarations.declare_variable(
gjc@3408
   255
                    'ns3::Ptr<%s>' % self.PYTHON_CALLBACK_IMPL_NAME,
gjc@3408
   256
                    '%s_cb_impl' % self.name)
gjc@3408
   257
                wrapper.before_call.write_code("%s = ns3::Create<%s> (%s);"
gjc@3408
   258
                                               % (callback_impl, self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
gjc@3408
   259
                wrapper.call_params.append(
gjc@3408
   260
                    'ns3::Callback<%s> (%s)' % (', '.join(self.TEMPLATE_ARGS), callback_impl))
gjc@3408
   261
gjc@3408
   262
            def convert_c_to_python(self, wrapper):
gjc@3408
   263
                raise typehandlers.NotSupportedError("Reverse wrappers for ns3::Callback<...> types "
gjc@3408
   264
                                                     "(python using callbacks defined in C++) not implemented.")
gjc@3408
   265
gjc@3408
   266
gjc@3408
   267
# def write_preamble(out):
gjc@3408
   268
#     pybindgen.write_preamble(out)
gjc@3408
   269
#     out.writeln("#include \"ns3/everything.h\"")
gjc@3408
   270
gjc@3408
   271
gjc@3408
   272
gjc@3408
   273
def Simulator_customizations(module):
gjc@3408
   274
    Simulator = module['ns3::Simulator']
gjc@3408
   275
gjc@3408
   276
    ## Simulator::Schedule(delay, callback, ...user..args...)
gjc@3408
   277
    Simulator.add_custom_method_wrapper("Schedule", "_wrap_Simulator_Schedule",
gjc@3408
   278
                                        flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
gjc@3408
   279
gjc@3408
   280
gjc@3408
   281
    ## Simulator::ScheduleNow(callback, ...user..args...)
gjc@3408
   282
    Simulator.add_custom_method_wrapper("ScheduleNow", "_wrap_Simulator_ScheduleNow",
gjc@3408
   283
                                        flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
gjc@3408
   284
gjc@3408
   285
gjc@3408
   286
    ## Simulator::ScheduleDestroy(callback, ...user..args...)
gjc@3408
   287
    Simulator.add_custom_method_wrapper("ScheduleDestroy", "_wrap_Simulator_ScheduleDestroy",
gjc@3408
   288
                                        flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
gjc@3408
   289
gjc@4086
   290
    Simulator.add_custom_method_wrapper("Run", "_wrap_Simulator_Run",
gjc@4086
   291
                                        flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
gjc@4086
   292
gjc@3408
   293
gjc@3408
   294
def CommandLine_customizations(module):
gjc@3408
   295
    CommandLine = module['ns3::CommandLine']
gjc@3408
   296
    CommandLine.add_method('Parse', None, [ArgvParam(None, 'argv')],
gjc@3408
   297
                           is_static=False)
gjc@3929
   298
    CommandLine.add_custom_method_wrapper("AddValue", "_wrap_CommandLine_AddValue",
gjc@3929
   299
                                          flags=["METH_VARARGS", "METH_KEYWORDS"])
gjc@3408
   300
gjc@3408
   301
gjc@3408
   302
def Object_customizations(module):
gjc@3408
   303
    ## ---------------------------------------------------------------------
gjc@3408
   304
    ## Here we generate custom constructor code for all classes that
gjc@3408
   305
    ## derive from ns3::Object.  The custom constructors are needed in
gjc@3408
   306
    ## order to support kwargs only and to translate kwargs into ns3
gjc@3408
   307
    ## attributes, etc.
gjc@3408
   308
    ## ---------------------------------------------------------------------
gjc@3408
   309
    Object = module['ns3::Object']
gjc@3408
   310
gjc@3408
   311
gjc@3408
   312
    ## add a GetTypeId method to all generatd helper classes
gjc@3408
   313
    def helper_class_hook(helper_class):
gjc@3408
   314
        decl = """
gjc@3408
   315
static ns3::TypeId GetTypeId (void)
gjc@3408
   316
{
gjc@3408
   317
  static ns3::TypeId tid = ns3::TypeId ("%s")
gjc@3408
   318
    .SetParent< %s > ()
gjc@3408
   319
    ;
gjc@3408
   320
  return tid;
gjc@3408
   321
}"""  % (helper_class.name, helper_class.class_.full_name)
gjc@3408
   322
gjc@3408
   323
        helper_class.add_custom_method(decl)
gjc@3408
   324
        helper_class.add_post_generation_code(
gjc@3408
   325
            "NS_OBJECT_ENSURE_REGISTERED (%s);" % helper_class.name)
gjc@3408
   326
    Object.add_helper_class_hook(helper_class_hook)
gjc@3408
   327
mathieu@4554
   328
    ## Replace all class constructors with a generic constructor based on CreateObjectWithAttributes<T> (AttributeList)
gjc@3408
   329
    module.header.writeln('''
gjc@3408
   330
namespace ns3 {
gjc@3408
   331
gjc@3408
   332
void PythonCompleteConstruct (Ptr<Object> object, TypeId typeId, const AttributeList &attributes);
gjc@3408
   333
gjc@3408
   334
template <typename T>
gjc@3408
   335
Ptr<T> CreateObjectPython (PyObject *pyobj, const AttributeList &attributes)
gjc@3408
   336
{
gjc@3408
   337
  Ptr<T> p = Ptr<T> (new T (), false);
gjc@3408
   338
  p->set_pyobj (pyobj);
gjc@3408
   339
  PythonCompleteConstruct (p, T::GetTypeId (), attributes);
gjc@3408
   340
  return p;  
gjc@3408
   341
}
gjc@3408
   342
gjc@3408
   343
} // namespace ns3
gjc@3408
   344
gjc@3408
   345
''')
gjc@3408
   346
    
gjc@3408
   347
    for cls in module.classes:
gjc@3408
   348
        if not cls.is_subclass(Object):
gjc@3408
   349
            continue
gjc@3408
   350
        cls.constructors = [] # clear the list of constructors
gjc@3408
   351
gjc@3408
   352
        ## add our own custom constructor, if possible
gjc@3408
   353
        try:
gjc@3408
   354
            construct_name = cls.get_construct_name()
gjc@3408
   355
        except CodeGenerationError:
gjc@3408
   356
            construct_name = None
gjc@3408
   357
gjc@3408
   358
        if construct_name and not cls.helper_class:
gjc@3408
   359
            construct_code = '''
mathieu@4554
   360
    ns3::Ptr< %(CONSTRUCT_NAME)s > obj = ns3::CreateObjectWithAttributes< %(CONSTRUCT_NAME)s > (attrList);
gjc@3408
   361
    obj->Ref ();
gjc@3408
   362
    self->obj = ns3::PeekPointer (obj);
gjc@3408
   363
''' % dict (CONSTRUCT_NAME=construct_name)
gjc@3408
   364
gjc@3408
   365
        elif not construct_name and not cls.helper_class:
gjc@3408
   366
            continue
gjc@3408
   367
gjc@3408
   368
        elif not construct_name and cls.helper_class:
gjc@3408
   369
            construct_code = '''
gjc@3408
   370
    if (self->ob_type != &%(PYTYPESTRUCT)s)
gjc@3408
   371
    {
gjc@3408
   372
        ns3::Ptr< %(HELPER_CLASS_NAME)s > obj = ns3::CreateObjectPython< %(HELPER_CLASS_NAME)s > ((PyObject *)self, attrList);
gjc@3408
   373
        obj->Ref ();
gjc@3408
   374
        self->obj = ns3::PeekPointer (obj);
gjc@3408
   375
    } else {
gjc@3408
   376
        PyErr_SetString(PyExc_TypeError, "Class cannot be constructed (unless subclassed)");
gjc@3408
   377
        {
gjc@3408
   378
            PyObject *exc_type, *traceback;
gjc@3408
   379
            PyErr_Fetch(&exc_type, return_exception, &traceback);
gjc@3408
   380
            Py_XDECREF(exc_type);
gjc@3408
   381
            Py_XDECREF(traceback);
gjc@3408
   382
        }
gjc@3408
   383
        return -1;
gjc@3408
   384
    }
gjc@3408
   385
''' % dict (CONSTRUCT_NAME=construct_name, HELPER_CLASS_NAME=cls.helper_class.name,
gjc@3408
   386
            PYTYPESTRUCT=cls.pytypestruct)
gjc@3408
   387
gjc@3408
   388
        elif construct_name and cls.helper_class:
gjc@3408
   389
            construct_code = '''
gjc@3408
   390
    if (self->ob_type != &%(PYTYPESTRUCT)s)
gjc@3408
   391
    {
gjc@3408
   392
        ns3::Ptr< %(HELPER_CLASS_NAME)s > obj = ns3::CreateObjectPython< %(HELPER_CLASS_NAME)s > ((PyObject *)self, attrList);
gjc@3408
   393
        obj->Ref ();
gjc@3408
   394
        self->obj = ns3::PeekPointer (obj);
gjc@3408
   395
    } else {
mathieu@4554
   396
        ns3::Ptr< %(CONSTRUCT_NAME)s > obj = ns3::CreateObjectWithAttributes< %(CONSTRUCT_NAME)s > (attrList);
gjc@3408
   397
        obj->Ref ();
gjc@3408
   398
        self->obj = ns3::PeekPointer (obj);
gjc@3408
   399
    }
gjc@3408
   400
''' % dict (CONSTRUCT_NAME=construct_name, HELPER_CLASS_NAME=cls.helper_class.name,
gjc@3408
   401
            PYTYPESTRUCT=cls.pytypestruct)
gjc@3408
   402
        else:
gjc@3408
   403
            raise AssertionError
gjc@3408
   404
gjc@3573
   405
        wrapper_name = "_wrap_create_object_%s" % (cls.mangled_full_name,) 
gjc@3408
   406
        constructor = '''
gjc@3408
   407
static int %(WRAPPER_NAME)s (%(PYSTRUCT)s *self, PyObject *args, PyObject *kwargs, PyObject **return_exception)
gjc@3408
   408
{
gjc@3408
   409
    if (PyTuple_Size(args)) {
gjc@3408
   410
        PyErr_SetString(PyExc_TypeError, "positional arguments not supported "
gjc@3408
   411
                        "for ns3.Object constructors, only keyword arguments"
gjc@3408
   412
                        " should be used (AttributeName=Value)");
gjc@3408
   413
        {
gjc@3408
   414
            PyObject *exc_type, *traceback;
gjc@3408
   415
            PyErr_Fetch(&exc_type, return_exception, &traceback);
gjc@3408
   416
            Py_XDECREF(exc_type);
gjc@3408
   417
            Py_XDECREF(traceback);
gjc@3408
   418
        }
gjc@3408
   419
        return -1;
gjc@3408
   420
    }
gjc@3408
   421
    ns3::AttributeList attrList;
gjc@3408
   422
    if (kwargs && KwargsToAttributeList(kwargs, %(CLASS_NAME)s::GetTypeId(), attrList)) {
gjc@3408
   423
        {
gjc@3408
   424
            PyObject *exc_type, *traceback;
gjc@3408
   425
            PyErr_Fetch(&exc_type, return_exception, &traceback);
gjc@3408
   426
            Py_XDECREF(exc_type);
gjc@3408
   427
            Py_XDECREF(traceback);
gjc@3408
   428
        }
gjc@3408
   429
        return -1;
gjc@3408
   430
    }
gjc@3408
   431
    %(CONSTRUCT_CODE)s
gjc@3770
   432
    PyNs3ObjectBase_wrapper_registry[(void *) self->obj] = (PyObject *) self;
gjc@3408
   433
    return 0;
gjc@3408
   434
}
gjc@3408
   435
''' % dict(WRAPPER_NAME=wrapper_name, PYSTRUCT=cls.pystruct, CLASS_NAME=cls.full_name,
gjc@3408
   436
           CONSTRUCT_CODE=construct_code, PURE_VIRTUALS=cls.have_pure_virtual_methods)
gjc@3408
   437
        cls.add_constructor(CustomCppConstructorWrapper(wrapper_name, constructor))
gjc@3408
   438
gjc@3408
   439
gjc@3408
   440
    # Generate conversion function from PyObject* to AttributeValue
gjc@3408
   441
#     sink = module.body
gjc@3408
   442
#     sink.writeln('''
gjc@3408
   443
# Ptr<AttributeValue> AttributeValueFromPyObject (PyObject *obj)
gjc@3408
   444
# {
gjc@3408
   445
#     // note: needs to check for bool first, because bool is a subclass of int
gjc@3408
   446
#     if (PyBool_Check(obj)) {
gjc@3408
   447
#         return Create<BooleanValue>(PyObject_IsTrue(obj));
gjc@3408
   448
#     } else if (PyInt_Check(obj)) {
gjc@3408
   449
#         return Create<IntegerValue>(PyInt_AsLong(obj));
gjc@3408
   450
#     } else if (PyLong_Check(obj)) {
gjc@3408
   451
#         return Create<IntegerValue>(PyLong_AsLongLong(obj));
gjc@3408
   452
#     } else if (PyFloat_Check(obj)) {
gjc@3408
   453
#         return Create<DoubleValue>(PyFloat_AsDouble(obj));
gjc@3408
   454
#     }
gjc@3408
   455
gjc@3408
   456
# ''')
gjc@3408
   457
    
gjc@3408
   458
gjc@3408
   459
gjc@3408
   460
    ## ---------------------------------------------------------------------
gjc@3408
   461
    ## -------------- write the KwargsToAttributeList function -------------
gjc@3408
   462
    ## ---------------------------------------------------------------------
gjc@3408
   463
    Attribute = module['ns3::AttributeValue']
gjc@3408
   464
    module.after_forward_declarations.writeln(
gjc@3408
   465
        'int KwargsToAttributeList(PyObject *kwargs, ns3::TypeId tid, ns3::AttributeList &oAttrList);')
gjc@3408
   466
gjc@3408
   467
    module.body.writeln(
gjc@3408
   468
'''
gjc@3408
   469
int KwargsToAttributeList(PyObject *kwargs, ns3::TypeId tid, ns3::AttributeList &oAttrList)
gjc@3408
   470
{
gjc@3408
   471
    PyObject *key, *value;
gjc@3408
   472
    Py_ssize_t pos = 0;
gjc@3408
   473
gjc@3408
   474
    while (PyDict_Next(kwargs, &pos, &key, &value)) {
gjc@3408
   475
        if (!PyString_Check(key)) {
gjc@3408
   476
            PyErr_SetString(PyExc_TypeError, "kwargs keys must be strings");
gjc@3408
   477
            return -1;
gjc@3408
   478
        }
gjc@3408
   479
        if (PyObject_IsInstance(value, (PyObject*) &%s)) {
gjc@3408
   480
            oAttrList.SetWithTid(tid, PyString_AsString(key), *((%s *) value)->obj);''' \
gjc@3408
   481
    % (Attribute.pytypestruct, Attribute.pystruct))
gjc@3408
   482
gjc@3408
   483
    for conversion_source in Attribute.get_all_implicit_conversions():
gjc@3408
   484
        module.body.writeln('''
gjc@3408
   485
        } else if (PyObject_IsInstance(value, (PyObject*) &%s)) {
gjc@3408
   486
            oAttrList.SetWithTid(tid, PyString_AsString(key), *((%s *) value)->obj);''' \
gjc@3408
   487
                        % (conversion_source.pytypestruct, conversion_source.pystruct))
gjc@3408
   488
gjc@3408
   489
    possible_type_names = ", ".join([cls.name for cls in [Attribute] + Attribute.get_all_implicit_conversions()])
gjc@3408
   490
    module.body.writeln('''
gjc@3408
   491
        } else {
gjc@3408
   492
            PyErr_Format(PyExc_TypeError, \"parameter must an instance of one of the types (%s), not %%s\", value->ob_type->tp_name);
gjc@3408
   493
            return -1;
gjc@3408
   494
        }''' % (possible_type_names))
gjc@3408
   495
gjc@3408
   496
    module.body.writeln(
gjc@3408
   497
'''
gjc@3408
   498
    }
gjc@3408
   499
    return 0;
gjc@3408
   500
}
gjc@3408
   501
''')
gjc@3421
   502
gjc@3421
   503
gjc@3421
   504
def Attribute_customizations(module):
gjc@3421
   505
    # Fix up for the "const AttributeValue &v = EmptyAttribute()"
gjc@3421
   506
    # case, as used extensively by helper classes.
gjc@3421
   507
gjc@3421
   508
    # Here's why we need to do this: pybindgen.gccxmlscanner, when
gjc@3421
   509
    # scanning parameter default values, is only provided with the
gjc@3421
   510
    # value as a simple C expression string.  (py)gccxml does not
gjc@3421
   511
    # report the type of the default value.
gjc@3421
   512
gjc@3421
   513
    # As a workaround, here we iterate over all parameters of all
gjc@3421
   514
    # methods of all classes and tell pybindgen what is the type of
gjc@3421
   515
    # the default value for attributes.
gjc@3421
   516
gjc@3421
   517
    for cls in module.classes:
gjc@3421
   518
        for meth in cls.get_all_methods():
gjc@3421
   519
            for param in meth.parameters:
gjc@3421
   520
                if isinstance(param, cppclass.CppClassRefParameter):
gjc@3421
   521
                    if param.cpp_class.name == 'AttributeValue' \
gjc@3421
   522
                            and param.default_value is not None \
gjc@3421
   523
                            and param.default_value_type is None:
gjc@3421
   524
                        param.default_value_type = 'ns3::EmptyAttributeValue'
gjc@3421
   525
gjc@3753
   526
gjc@3753
   527
def TypeId_customizations(module):
gjc@3753
   528
    TypeId = module['ns3::TypeId']
gjc@3753
   529
    TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",
gjc@3753
   530
                                     flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
gjc@3753
   531
gjc@3929
   532
gjc@4196
   533
def add_std_ofstream(module):
gjc@4196
   534
    module.add_include('<fstream>')
gjc@4196
   535
    ostream = module.add_class('ostream', foreign_cpp_namespace='::std')
gjc@4196
   536
    ostream.set_cannot_be_constructed("abstract base class")
gjc@4196
   537
    ofstream = module.add_class('ofstream', foreign_cpp_namespace='::std', parent=ostream)
gjc@4196
   538
    ofstream.add_enum('openmode', [
gjc@4196
   539
            ('app', 'std::ios_base::app'),
gjc@4196
   540
            ('ate', 'std::ios_base::ate'),
gjc@4196
   541
            ('binary', 'std::ios_base::binary'),
gjc@4196
   542
            ('in', 'std::ios_base::in'),
gjc@4196
   543
            ('out', 'std::ios_base::out'),
gjc@4196
   544
            ('trunc', 'std::ios_base::trunc'),
gjc@4196
   545
            ])
gjc@4196
   546
    ofstream.add_constructor([Parameter.new("const char *", 'filename'),
gjc@4196
   547
                              Parameter.new("::std::ofstream::openmode", 'mode', default_value="std::ios_base::out")])
gjc@4196
   548
    ofstream.add_method('close', None, [])
gjc@4196
   549