* Martin v. Loewis, on 08.07.2010 07:23:
And since things work for a single method when I declare 'def' as
'static', I suspect that means that the function object created by
PyCFunction_NewEx holds on to a pointer to the PyMethodDef structure?

Correct; it doesn't make a copy of the struct. So when you want the
function object to outlive the setRoutine call, you need to allocate
the PyMethodDef on the heap.

Thanks!

That's the direction I tentatively had started to investigate.

But second problem now is cleanup: I'd like to deallocate when the module is freed. I tried (1) adding a __del__, but no dice, I guess because it wasn't really an object method but just a free function in a module; and (2) the m_free callback in the module definition structure, but it was not called.

Perhaps I don't need to to clean up?

Anyway, current looks like this:


<code>
// progrock.cppy  --  "C++ plus Python"
// A simple C++ framework for writing Python 3.x extensions.
//
// Copyright (C) Alf P. Steinbach, 2010.

#ifndef CPPY_MODULE_H
#define CPPY_MODULE_H
#include <progrock/cppx/devsupport/better_experience.h>


//----------------------------------------- Dependencies:

#include "Ptr.h"
#include <progrock/cppx/exception/throwing.h>
#include <list>



//----------------------------------------- Interface:

namespace progrock{ namespace cppy {

    namespace detail {
        inline PyModuleDef* moduleDefPtr()
        {
            static PyMethodDef methodDefs[] = {
                //...
//{ "system", &pyni_system, METH_VARARGS, "Execute a shell command." },
                //{ "__del__", &onModuleDestroy, METH_NOARGS, "Destructor" },
                //...
                { NULL, NULL, 0, NULL }     // Sentinel
            };

            static PyModuleDef moduleDef = {
                PyModuleDef_HEAD_INIT,
                "cppy",     // name of module
                NULL,       // m_doc,   // module documentation in UTF-8
sizeof(void*), // size of per-interpreter state of the module, // or -1 if the module keeps state in global variables.
                methodDefs,
                NULL,       // m_reload
                NULL,       // m_traverse
                NULL,       // m_clear
                NULL        // m_free
            };

            return &moduleDef;
        }
    }

    class Module
    {
    private:
        struct InstanceData
        {
            std::list< PyMethodDef >    methodDefs;
        };

        void* instanceMemory() const
        {
            void* const result = PyModule_GetState( p_.get() );
            assert( result != 0 );
            return result;
        }

        InstanceData*& instanceDataPtr() const
        {
            return *reinterpret_cast< InstanceData** >( instanceMemory() );
        }

        Ptr             p_;
        InstanceData*   data_;

    public:
        Module()
            : p_( ::PyModule_Create( detail::moduleDefPtr() ) )
        {
            assert( def.m_size == sizeof( void* ) );
            (p_.get() != 0) || cppx::throwX( "Module::<init>: failed" );
            instanceDataPtr() = data_ = new InstanceData();
        }

        PyObject* rawPtr() const    { return p_.get(); }
        PyObject* release()         { return p_.release(); }

        void setDocString( wchar_t const s[] )
        {
            Ptr const v = ::PyUnicode_FromWideChar( s, -1 );
            (v.get() != 0)
|| cppx::throwX( "Module::setDocString: PyUnicode_FromWideChar failed" );
            ::PyObject_SetAttrString( p_.get(), "__doc__", v.get() )
                >> cppx::is( cppx::notMinusOne )
|| cppx::throwX( "Module::setDocString: PyObject_SetAttrString failed" );
        }

        void addRoutine( char const name[], PyCFunction f, char const doc[] = 
"" )
        {
            PyMethodDef const defData = { name, f, METH_VARARGS, doc };

            data_->methodDefs.push_back( defData );
            try
            {
                PyMethodDef*    pDef    = &data_->methodDefs.back();

                Ptr const   pyName  = ::PyUnicode_FromString( name );
Ptr r = ::PyCFunction_NewEx( pDef, p_.get(), pyName.get());

                ::PyModule_AddObject( p_.get(), name, r.release() )
                    >> cppx::is( cppx::notMinusOne )
|| cppx::throwX( "Module::addRoutine: PyModule_AddObject failed" );
            }
            catch( ... )
            {
                data_->methodDefs.pop_back();
                throw;
            }
        }
    };

} }  // namespace progrock::cppy


#endif
</code>


I changed the module name from "pyni*" to "cppy"... ;-)


Cheers & thanks!, but how to clean up, or must I?

- Alf

--
blog at <url: http://alfps.wordpress.com>
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to