[C++-sig] Re: Using .add_property with make_getter.

Kirsebom Nikolai nikolai.kirsebom at siemens.no
Tue Jun 24 14:37:37 CEST 2003


> -----Original Message-----
> From: David Abrahams [mailto:dave at boost-consulting.com]
> Sent: 24. juni 2003 13:23
> To: c++-sig at python.org
> Subject: ÆC++-sigÅ Re: Using .add_property with make_getter.
> 
> 
> Kirsebom Nikolai <nikolai.kirsebom at siemens.no> writes:
> 
> > I have the following c++ class:
> > class PyDLEPRInterface : public DLEPRInterface
> > {
> > public:
> > 	PyDLEPRInterface();
> > 	PyDLEPRInterface(const PyDLEPRInterface& objectSrc);
> > };
> >
> > PyDLEPRInterface::PyDLEPRInterface(const PyDLEPRInterface& 
> objectSrc)
> > {
> > }
> >
> > PyDLEPRInterface::PyDLEPRInterface()
> > {
> > }
> >
> > The class DLEPRInterface defines some public attributes, example
> > 	CString m_DocumentCategory;
> > 	int m_UserID;
> >
> >
> > I have a converter for converting CString <--> python string.
> > See code in thread "Exception second time loaded" 20th of June.
> >
> > The two attributes are exposed with the statements:
> > 	.def_readonly("UserID", &PyDLEPRInterface::m_UserID)
> > and
> > 	.add_property("DocumentCategory",
> > make_getter(&PyDLEPRInterface::m_DocumentCategory,
> > return_value_policy<return_by_value>()))
> >
> > When running in Python, the UserID is available 
> 
> What does "available" mean?

Only that I'm able to read it's value from Python.

> 
> > however reading the DocumentCategory attribute produces the
> > following traceback:
> >
> > import DocuLive  #<<Module exposing function to get an existing
> > PyDELEPRInterface object
> > import CString #<<Module handling the conversion
> > v = DocuLive.getit() #<<Fetch the exisiting object
> > v.UserID #<<Print the value for UserID attribute
> > 44
> > v.DocumentCategory 
> > Traceback (most recent call last):
> >   File "<input>", line 1, in ?
> > TypeError: bad argument type for built-in operation
> 
> Can you post a complete, minimal test case that we can use to
> reproduce the problem?
> 

I'll try to make a test-case.  Problem with the current system is that it
includes a lot of propriatory code  that I cannot provide.


> > I'm running the python statements in a PyCrust (wxPython/wxWindows)
> > shell application.  In my posting 20th of June I asked for help
> > relating to exception when staring the second time.  It appears that
> > the starting of the mainloop in the PyCrust application produces the
> > exception if other applications (in Windows) has been activated in
> > between.  
> 
> I don't know what that means, but I can tell you that if you want to
> initialize the same Boost.Python extension modules a 2nd time, the
> Boost.Python DLL must be unloaded first.  I don't know what it takes
> to do that, but I'm guessing we have a problem because it gets
> referenced by each of the BPL extension modules which is loaded, and
> they in turn are being kept alive because no PyFinalize() is being
> called, because we don't support PyFinalize() yet.  Dirk Gerrits has
> been working on a solution, but has been waylaid.  It's an important
> feature to get implemented, but Boost Consulting has to focus on
> projects which have been funded.
> 
> 

What code actually initiates the Boost.Python initialization ?  My extension
DLL is not unloaded, at least when running in the debugger (VS 7.0) the
Modules windows lists the DLLs (Python22.dll, boost_python_debug.dll and
DLEPRPythonDld.dll (mine)).

I enclose my 'module' file.  Maybe the solution is obvious to someone.  I'll
be away for some time and I'll look into making a 'workable' test-case when
back.  Untill then, thanks for your quick reply and help.

PS: Could the problem (getting access to the property) be related to the
fact that I wrap the actual instance object in a new class PyDLEPRInterface
inheriting from DLEPRInterface, something I had to do because of compiler
error (see my posting on the 20th of June (Exception second time loaded). 

PPS: The CPythonDocuLiveDlg dialog is executed with the DoModal().

PPPS: I've not been able to retreive the exception information when the
statement "wxPython.lib.PyCrust.PyShellApp.main()" fails.  So I've made a
counter to indicate where the it failed, an 'i' comes out with the value 2.

Nikolai Kirsebom


////////////////////////////////////////////////////////////////////////////
////////////////////////////////
// 
//

#include "stdafx.h"
#include "resource.h"

#include <DLEPRInterface.h>
#include <DLdbAPI.h>

#include "PyInterface.h"

//#include "Python.h"
#include <boost/python/dict.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/handle.hpp>
#include <boost/python/to_python_converter.hpp>

#include <boost/python/detail/wrap_python.hpp>
#include <boost/python.hpp>

////////////////////////////////////////////////////////////////////////////
//////////////
// 

class PyDLEPRInterface : public DLEPRInterface
{
public:
	PyDLEPRInterface();
	PyDLEPRInterface(const PyDLEPRInterface& objectSrc);
};

PyDLEPRInterface::PyDLEPRInterface(const PyDLEPRInterface& objectSrc)
{
}

PyDLEPRInterface::PyDLEPRInterface()
{
}


////////////////////////////////////////////////////////////////////////////
/////////////
///// EXPOSED INTERFACE
/////////////////////////////////////////////////////////////////

using namespace boost::python;

static PyDLEPRInterface* curr=NULL;
static bool ThreadRunning = FALSE;

class PythonException
{
    std::string     m_exception_type;
    std::string     m_error_message;

public:
    PythonException():m_exception_type(""),m_error_message(""){};

    void setExceptionType(std::string msg) {m_exception_type = msg;}
    void setErrorMessage(std::string msg) {m_error_message = msg;}
    std::string getExceptionType() {return m_exception_type;}
    std::string getErrorMessage(void) {return m_error_message;}
};

void getExceptionDetail(PythonException& exc)
{
    PyObject*   exc_type;
    PyObject*   exc_value;
    PyObject*   exc_traceback;
    PyObject*   pystring;

    PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
    if( exc_type==0 && exc_value==0 && exc_traceback==0)
    {
        exc.setExceptionType("Strange: No Python exception occured");
        exc.setErrorMessage("Strange: Nothing to report");
    }
    else
    {
        pystring = NULL;
        if (exc_type != NULL &&
           (pystring = PyObject_Str(exc_type)) != NULL &&     /* str(object)
*/
           (PyString_Check(pystring))
           )
            exc.setExceptionType(PyString_AsString(pystring));
        else
            exc.setExceptionType("<unknown exception type>");

        Py_XDECREF(pystring);

        pystring = NULL;
        if (exc_value != NULL &&
           (pystring = PyObject_Str(exc_value)) != NULL &&     /*
str(object) */
           (PyString_Check(pystring))
           )
            exc.setErrorMessage(PyString_AsString(pystring));
        else
            exc.setErrorMessage("<unknown exception data>");

        Py_XDECREF(pystring);

        Py_XDECREF(exc_type);
        Py_XDECREF(exc_value);         /* caller owns all 3 */
        Py_XDECREF(exc_traceback);     /* already NULL'd out */
    }
}

PyDLEPRInterface* getit() {
	return curr;
}

BOOST_PYTHON_MODULE(DocuLive)
{
	def("getit", getit,
return_value_policy<reference_existing_object>())
	;

	class_<PyDLEPRInterface>("DLEPRInterface")
		.def("GetDatabaseName", &PyDLEPRInterface::GetDatabaseName)
		.def("GetDefaultServerName",
&PyDLEPRInterface::GetDefaultServerName)
		.def("IsRubber", &PyDLEPRInterface::IsRubber)

		.def_readonly("RecordId", &PyDLEPRInterface::m_RecordID)
		.def_readonly("Item", &PyDLEPRInterface::m_Item)
		.def_readonly("OriginalItem",
&PyDLEPRInterface::m_OriginalItem)
		.def_readonly("Row", &PyDLEPRInterface::m_Row)
		.def_readonly("Col", &PyDLEPRInterface::m_Col)
		.def_readonly("UserID", &PyDLEPRInterface::m_UserID)
		.def_readonly("m_CurMenuEntry",
&PyDLEPRInterface::m_CurMenuEntry)

        .add_property("DocumentCategory",
make_getter(&PyDLEPRInterface::m_DocumentCategory,
return_value_policy<return_by_value>()))
        .add_property("LookupCategory",
make_getter(&PyDLEPRInterface::m_LookupCategory,
return_value_policy<return_by_value>()))
        .add_property("RecordCategory",
make_getter(&PyDLEPRInterface::m_RecordCategory,
return_value_policy<return_by_value>()))
        .add_property("IconPurpose",
make_getter(&PyDLEPRInterface::m_IconPurpose,
return_value_policy<return_by_value>()))
	;

	class_<MenuEntry>("MenuEntry")
		.add_property("ParamString1",
make_getter(&MenuEntry::ParamString1,
return_value_policy<return_by_value>()))
	;
}

namespace MFCString { namespace {

  struct CString_to_python_str
  {
    static PyObject* convert(CString const& s)
    {
      CString ss = s;
      std::string x = ss.GetBuffer(1000);
      return boost::python::incref(boost::python::object(x).ptr());
    }
  };

  struct CString_from_python_str
  {
    CString_from_python_str()
    {
      boost::python::converter::registry::push_back(
        &convertible,
        &construct,
        boost::python::type_id<CString>());
    }

    static void* convertible(PyObject* obj_ptr)
    {
      if (!PyString_Check(obj_ptr)) return 0;
      return obj_ptr;
    }

    static void construct(
      PyObject* obj_ptr,
      boost::python::converter::rvalue_from_python_stage1_data* data)
    {
      const char* value = PyString_AsString(obj_ptr);
      if (value == 0) boost::python::throw_error_already_set();
      void* storage =
((boost::python::converter::rvalue_from_python_storage<CString>*)data)->stor
age.bytes;
      new (storage) CString(value);
      data->convertible = storage;
    }
  };

  void init_module()
  {
    using namespace boost::python;

    boost::python::to_python_converter<
      CString,
      CString_to_python_str>();

    CString_from_python_str();
  }
}} // namespace MFCString::<anonymous>

BOOST_PYTHON_MODULE(CString)
{
  MFCString::init_module();
}

BOOST_PYTHON_MODULE(MFC)
{
	class_<CRect>("CRect")
		.def_readwrite("bottom", &CRect::bottom)
		.def_readwrite("top", &CRect::top)
		.def_readwrite("right", &CRect::right)
		.def_readwrite("left", &CRect::left)
		.def("Height", &CRect::Height)
		.def("Width", &CRect::Width)
	;

	class_<CPoint>("CPoint")
		.def_readwrite("x", &CPoint::x)
		.def_readwrite("y", &CPoint::y)
	;
}

////////////////////////////////////////////////////////////////////////////
////////
/// PYTHON EXECUTOR CLASS
//////////////////////////////////////////////////////////
class PyExecutor
{
public:
	PyExecutor();
	~PyExecutor();
	CString RunStmt(DLEPRInterface * dl);
	PyObject * MainNamespace;
};

PyExecutor::PyExecutor()
{
    // Register the module with the interpreter
    if (PyImport_AppendInittab("DocuLive", initDocuLive) == -1)
        throw std::runtime_error("Failed to add DocuLive to the
interpreter's builtin modules");

    if (PyImport_AppendInittab("CString", initCString) == -1)
        throw std::runtime_error("Failed to add CString to the interpreter's
builtin modules");
	
    if (PyImport_AppendInittab("MFC", initMFC) == -1)
        throw std::runtime_error("Failed to add MFC to the interpreter's
builtin modules");

	Py_Initialize();

	boost::python::handle<> main_module(borrowed(
PyImport_AddModule("__main__")));
	boost::python::handle<> main_namespace(borrowed(
PyModule_GetDict(main_module.get()) ));
	MainNamespace = main_namespace.get();
}

PyExecutor::~PyExecutor()
{
	Py_Finalize();
}

CString PyExecutor::RunStmt(DLEPRInterface * dlepr)
{
	PyObject *p = NULL;
	curr = (PyDLEPRInterface *)dlepr;

    PythonException     p_exc;      // Create empty exception object on
stack

    try
    {
		boost::python::handle<> result(PyRun_String(
			"try:\n"
			"  i = 1\n"
			"  import wxPython.lib.PyCrust.PyShellApp\n"
			"  i += 1\n"
			"  wxPython.lib.PyCrust.PyShellApp.main()\n"
			"  i += 1\n"
			"  del wxPython\n"
			"  i += 1\n"
			"  valx = 'ok'\n"
			"  i += 1\n"
			"except:\n"
			"  valx = 'error'\n"
			"  raise str(i)\n"
			"#except:\n"
			"#valx = 'error'\n",
			Py_file_input, MainNamespace, MainNamespace));

		result.reset();

		p = PyRun_String("valx", Py_eval_input, MainNamespace,
MainNamespace);
		result.release();
    }
    catch(error_already_set)          // What should we catch here??
    {
        getExceptionDetail(p_exc);
		std::string s1 = p_exc.getExceptionType();
		std::string s2 = p_exc.getErrorMessage();
		CString s;
		//s.Format("Exception: %s %s", s1, s2);
		AfxMessageBox(s);
		
        throw(p_exc);
    }

	if (p != NULL) {
		/**/
		char *s;
		int i = PyArg_Parse(p, "s", &s);
		return _T(s);
		/**/
	} else {
		return _T("NULL");
	}
}

////////////////////////////////////////////////////////////////////////////
///
//// THREAD SUPPORT FUNCTIONS
/////////////////////////////////////////////////

struct IfStruct {
		IfStruct(DLEPRInterface * i, CPythonDocuLiveDlg *dlg) { m_If
= i; m_Dlg = dlg; };
		DLEPRInterface * m_If;
		CPythonDocuLiveDlg * m_Dlg;
};

UINT ThreadFunc(LPVOID pParam)
{
	static PyExecutor *x = NULL;
	
	if (x == NULL) {
		x = new PyExecutor();
	}
	IfStruct * p = (IfStruct *) pParam;

	CString v = x->RunStmt(p->m_If);
	//Send close message to window
	p->m_Dlg->SendMessage(WM_CLOSE);
	return 0;
}

////////////////////////////////////////////////////////////////////////////
///
// CPythonDocuLiveDlg dialog

IMPLEMENT_DYNAMIC(CPythonDocuLiveDlg, CDialog)
CPythonDocuLiveDlg::CPythonDocuLiveDlg(DLEPRInterface* pInterface, CWnd*
pParent /*=NULL*/)
	: CDialog(CPythonDocuLiveDlg::IDD, pParent),m_Interface(*pInterface)
{
}

CPythonDocuLiveDlg::~CPythonDocuLiveDlg()
{
}

void CPythonDocuLiveDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BOOL CPythonDocuLiveDlg::OnInitDialog() 
{
	this->MoveWindow(0,0,100,100);  //Should be outside the screen
(-100,-100,100,100)
	IfStruct * s = new IfStruct(&m_Interface, this);
	m_Thread = AfxBeginThread(ThreadFunc, s);

	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CPythonDocuLiveDlg::OnOK() 
{
	CDialog::OnOK();
}

void CPythonDocuLiveDlg::OnCancel()
{
	CDialog::OnCancel();
}


BEGIN_MESSAGE_MAP(CPythonDocuLiveDlg, CDialog)
END_MESSAGE_MAP()

// CPythonDocuLiveDlg message handlers




More information about the Cplusplus-sig mailing list