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