From: Barry S. <ba...@ba...> - 2010-04-06 17:42:54
|
On 5 Apr 2010, at 19:07, Fernando Libonati wrote: > Dear all, > > I'm new to the list, and want to soy Hello! posting some questions. > > ------ > > I'm working on a project related to a block diagram simulator and we have to move the actual implementation (C++ library + SWIG interface) to PyCXX. > During the first approach to CXX, I've developed a simple class Block that accepts a parent (another Block) a name and a number ... > The two files used until now are based on simple.cxx: I'll comment inline > > The base.hxx file is > """ > #ifndef __BASE__ > #define __BASE__ > #include "CXX/Objects.hxx" > #include "CXX/Extensions.hxx" > #include "iostream" > #include "string" Its odd to use "header" and not <header> why do you do this? > > #define ENDL "\n"; Use std::endl as its in the standard. > > //class Block: public Py::PythonExtension< Block > > class Block: public Py::PythonClass< Block > > { > Py::Object parent; > Py::String name; > Py::Int number; > public: > //Block( Py::String name_, Py::Int number_, Py::Object parent_ ) > Block(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) > : Py::PythonClass< Block >::PythonClass( self, args, kwds ) > { > args.verify_length(3); > name = args[0]; > number = args[1]; > parent = args[2]; > //std::cout << "sizeof() = " << sizeof(Block) << ENDL; > } > > virtual ~Block() > { > //std::cout << "~Block "<<name<<" (auuughh)." << ENDL; > } > > static void init_type(void) > { > behaviors().name( "Block" ); > behaviors().doc( "Clase base para los bloques de simulacion" ); > //behaviors().supportGetattr() > ; > //behaviors().supportSetattr(); > behaviors().supportGetattro(); > behaviors().supportSetattro(); > > > PYCXX_ADD_NOARGS_METHOD( getFullName, getFullName, "obtener el nombre completo del bloque" ); > //add_varargs_method("getFullName",&Block::getFullName,"getFullName()"); > > // Call to make the type ready for use > behaviors().readyType(); > } > > //static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { > // return static_cast<PyObject*>(new Block(subtype,args,kwds)); > //} > //static void tp_dealloc(PyObject *obj) { > // Block* k = static_cast<Block*>(obj); > // Py::_XDECREF(*(k->parent)); > // Py::_XDECREF(*(k->name)); > // Py::_XDECREF(*(k->number)); > // //delete k; > // extension_object_deallocator(obj); > //} PyCXX writes this tp_new and the other necessary functions for you. > > //Py::Object getFullName( const Py::Tuple& args ) > Py::Object getFullName( void ) > { > std::string r = ""; > if( *parent != Py_None ) You can use parent->isNone(). > r += Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( "utf-8" ) + "."; You are freely mixing Pyhon C API and PyCXX. This means you have to worry about reference counting. Use the newly added callMemberFunction. Py::String( parent->callMemberFunction( "getFullName" ) ) > > //if( parent != Py_None ) r = r.concat(p.name).concat(Py::String(".")); > r += name.as_std_string( "utf-8" ); > return Py::String(r); > } > PYCXX_NOARGS_METHOD_DECL( Block, getFullName ) > > //Py::Object getattr( const char * name_ ) > Py::Object getattro( const Py::String &name_ ) > { > //std::string aname( name_ ); > std::string aname( name_.as_std_string( "utf-8" ) ); > > if( aname == "name" ) return name; > if( aname == "number" ) return number; > if( aname == "parent" ) return parent; > //else return getattr_methods( name_ );; > return genericGetAttro( name_ ); > } > > int setattro( const Py::String &name_, const Py::Object &value ) > { > std::string aname( name_.as_std_string( "utf-8" ) ); > > if( aname == "name" ) { > name = Py::String(value); > return 0; > } if( aname == "number" ) { > number = Py::Int(value); > return 0; > } if( aname == "parent" ) { > parent = value; > return 0; > } else { > return genericSetAttro( name_, value ); > } > } > > }; > > #endif // __BASE__ > """ > > The base.cxx file is: > """ > #include <assert.h> > #include "base.h" > > class base_module : public Py::ExtensionModule<base_module> > { > public: > base_module() > : Py::ExtensionModule<base_module>( "base" ) // this must be name of the file on disk e.g. simple.so or simple.pyd > { > Block::init_type(); > > //add_varargs_method("Block", &base_module::factory_Block, "Block()"); > // after initialize the moduleDictionary will exist > > initialize( "documentation for the simple module" ); > //Py::Dict d( moduleDictionary() ); > //d["var"] = Py::String( "var value" ); > Py::Object x( Block::type() ); > d["Block"] = x; > } > > virtual ~base_module() > {} > private: > /* Py::Object new_Block(const Py::Tuple& args) > { > if(args.length() != 3) throw Py::RuntimeError("Incorrect # of args to Block(name,number,parent)."); > //return Py::asObject(new Block(Py::String(args[0]),Py::Int(args[1]),Py::Object(args[2]))); > return Py::asObject(new Block(args)); > } > */ > }; > > #if defined( PY3 ) > static base_module *base; > > extern "C" PyObject *PyInit_base() > { > #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) > Py::InitialisePythonIndirectPy::Interface(); > #endif > > base = new base_module; > return base->module().ptr(); > } > > // symbol required for the debug version > extern "C" PyObject *PyInit_base_d() > { > return PyInit_base(); > } > > #else > > static base_module *base; > > extern "C" void initbase() > { > #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) > Py::InitialisePythonIndirectPy::Interface(); > #endif > > base = new base_module; > } > > // symbol required for the debug version > extern "C" void initbase_d() > { > initbase(); > } > #endif > """ > > I'm using trunk version of the CXX files. > The software works, creates the blocks, chain them, etc, but creating and destroying 1000000 base.Block objects finish with a 44MB memory usage (when 4MB is expected) (the sizeof(Block) is 40 bytes times 1000000 is 40MB !!!) > > I've tryed some variations creating Block as a PythonExtension successfully, an the memory doesn't grow with my 1000000 creation/deletion test process (no memory leak). > > This leads me to thinks that something in the deallocator for new_style_classes isn't working well. > What do you think? > > ---- > > Also, I've made the test with the simple.cxx module from Demo directory, and the same thing happens (as the object is ligthier you have to create/destroy 10000000, or so, times to see the memory growing). > Someone have any clue? Can you post the python code you used to test simple.cxx which to show the memory leak please? On what python version and operating system are you doing this work? Barry |