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:

The base.hxx file is
"""
#ifndef __BASE__
#define __BASE__
#include "CXX/Objects.hxx"
#include "CXX/Extensions.hxx"
#include "iostream"
#include "string"

#define ENDL "\n";

//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);
    //}

    //Py::Object getFullName( const Py::Tuple& args )
    Py::Object getFullName( void )
    {
        std::string r = "";
        if( *parent != Py_None )
            r += Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( "utf-8" ) + ".";

        //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?

Best regards,
Fernando Libonati