Opened 20 years ago

Closed 16 years ago

#138 closed Support Requests (Fixed)

problem with overridable virtual functions in Boost.Python

Reported by: nobody Owned by: david_abrahams
Milestone: Component: python USE GITHUB
Version: None Severity:
Keywords: Cc:

Description

I'm having problems overriding functions in Python with 
Boost. The examples provided on the website seem to work. 
However, all the examples I have managed to find have 
involved callback classes with virtual function overrides 
having zero arguments. I am trying to override functions 
with multiple arguments with no success.  I get consistent 
errors when trying to manipulate multi-argument virtual 
functions polymorphically from a C++ extension. Im 
currently using Visual Studio .Net and Python 2.2.2. The 
following code demostrates the problem:

------------------Begin Code ---------------------------

C++ code:

#include <iostream>
#include <string>

class Entity
{
public:
	Entity() {Class = "Entity"; }
	Entity(const Entity& e) { this->Class = e.Class; }
	std::string virtual GetClass(std::string PreFix) { 
return Class + PreFix; }
public:
	std::string Class;
};

class SimpleEntity : public Entity
{
public:
	SimpleEntity() { this->Class = "SimpleEntity"; }
	SimpleEntity(const SimpleEntity& se) { this-
>Class = se.Class; }
	std::string virtual GetClass(std::string PreFix) { 
return Class + PreFix; }
};

#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/args.hpp>
#include <boost/python/class.hpp>
#include <boost/python/overloads.hpp>
#include <boost/python/call_method.hpp>

using namespace boost::python;

std::string Test(Entity* e, std::string PreFix) {
	return e->GetClass(PreFix);
}

class EntityWrap : public Entity
{ 
public:
	EntityWrap(PyObject* self_) : self(self_)  {}
	EntityWrap(PyObject* self_, const Entity& e) : 
self(self_), Entity(e) {}
	std::string GetClass(std::string PreFix) { return 
call_method<std::string>(self, "GetClass", PreFix); }
	std::string default_GetClass( Entity& e, 
std::string PreFix) { return e.Entity::GetClass(PreFix); } 

private:
	PyObject* self;
};


class SimpleEntityWrap : public SimpleEntity
{ 
public:
	SimpleEntityWrap(PyObject* self_) : self(self_)  
{}
	SimpleEntityWrap(PyObject* self_, const 
SimpleEntity& se) : self(self_), SimpleEntity(se) {}
	std::string GetClass(std::string PreFix) { return 
call_method<std::string>(self, "GetClass", PreFix); }
	std::string default_GetClass( SimpleEntity& se, 
std::string PreFix) { return se.SimpleEntity::GetClass
(PreFix); } 

private:
	PyObject* self;

};

BOOST_PYTHON_MODULE(hello)
{

	class_<Entity, EntityWrap>("Entity")
		.def(init<const Entity&>())
		.def("GetClass", 
&EntityWrap::default_GetClass)
		.def_readwrite("Class", 
&Entity::Class);
	;

	class_<SimpleEntity, SimpleEntityWrap, 
bases<Entity> >("SimpleEntity")
		.def(init<const SimpleEntity&>())
		.def("GetClass", 
&SimpleEntityWrap::default_GetClass)
		;

	def("Test", Test);
}

Python Code:

Python 2.2.2 (#37, Oct 14 2002, 17:02:34) [MSC 32 bit 
(Intel)] on win32
Type "copyright", "credits" or "license" for more information.
IDLE 0.8 -- press F1 for help
>>> from hello import *
>>> 
class DerivedEntity(SimpleEntity):
	def __init__(self):
		SimpleEntity.Class = "DerivedEntity"
	def GetClass(self,x):
		return self.Class + x

	
>>> e = Entity()
>>> Test(e, " a test")
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in ?
    Test(e, " a test")
TypeError: bad argument type for built-in operation
>>> s = SimpleEntity()
>>> Test(s, " a test")
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in ?
    Test(s, " a test")
TypeError: bad argument type for built-in operation
>>> d = DerivedEntity()
>>> Test(d, " a test")
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in ?
    Test(d, " a test")
TypeError: bad argument type for built-in operation
>>> e.Class
'Entity'
>>> e.GetClass(" a test")
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in ?
    e.GetClass(" a test")
TypeError: bad argument type for built-in operation
>>> s.Class
'DerivedEntity'
>>> s.GetClass("a test")
Traceback (most recent call last):
  File "<pyshell#12>", line 1, in ?
    s.GetClass("a test")
TypeError: bad argument type for built-in operation
>>> d.Class
'DerivedEntity'
>>> d.GetClass(" a test")
'DerivedEntity a test'
>>> 


------------------End Code ---------------------------

Am I missing something? Any help would be appreciated.

Thanks, Vince

Change History (4)

comment:1 by Joel de Guzman, 20 years ago

Logged In: YES 
user_id=237817

Hi Vince,

The tutorial has been updated. There's a specific page now
that focuses on virtual functions with default implementation.
You can read it here:

http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/*checkout*/boost/boost/libs/python/doc/tutorial/doc/virtual_functions_with_default_implementations.html

Attached is the corrected code. Most notably:

    std::string default_GetClass(std::string PreFix) {
return Entity::GetClass(PreFix); }

... If you have to pass an object as the first argument, it
has to be a static function. A non-static function will do.

    std::string default_GetClass(std::string PreFix) {
return SimpleEntity::GetClass(PreFix); }

...Ditto.

        .def("GetClass", &Entity::GetClass,
&EntityWrap::default_GetClass)

...See the tutorial link that I gave above.

        .def("GetClass", &SimpleEntity::GetClass,
&SimpleEntityWrap::default_GetClass)

... Ditto

Finally...

>>> class DerivedEntity(SimpleEntity):
...     def __init__(self):
...         SimpleEntity.Class = "DerivedEntity"
...     def GetClass(self,x):
...         return self.Class + x

looks suspicious.  Doesn't it need to __init__ the base class?
See the attached corrections.

Regards,
--
Joel de Guzman
joel at boost-consulting.com
http://www.boost-consulting.com
http://spirit.sf.net



comment:2 by nobody, 20 years ago

Logged In: NO 

Thanks for the fix Joel, everything works as expected now. I 
really appreciate your prompt response. One last thing, I am 
assuming that this solution is only applicable to the version of 
boost currently in cvs? When I tried the same fix with version 
1.29 it generates a compile error.

Much Thanks,
Vince

The above mentioned error in case you weren't aware.

C:\Documents and Settings\thomasinov\Desktop\boost_1_29_0
\boost\python\detail\de
f_helper.hpp(131) : error C2440: 'return' : cannot convert 
from 'std::basic_stri
ng<_Elem,_Traits,_Ax> ' to 'const char *'
        with
        [
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Ax=std::allocator<char>
        ]
        There is no context in which this conversion is possible
        C:\Documents and 
Settings\thomasinov\Desktop\boost_1_29_0\boost\python\d
etail\def_helper.hpp(130) : while compiling class-template 
member function 'cons
t char *boost::python::detail::def_helper<T1,T2,T3>::doc(void) 
const'
        with
        [
            
T1=std::basic_string<char,std::char_traits<char>,std::allocator<c
har
>> (__thiscall EntityWrap::* )(std::string),
            T2=boost::python::detail::not_specified,
            T3=boost::python::detail::not_specified
        ]
        C:\Documents and 
Settings\thomasinov\Desktop\boost_1_29_0\boost\python\c
lass.hpp(362) : see reference to class template 
instantiation 'boost::python::de
tail::def_helper<T1,T2,T3>' being compiled
        with
        [
            
T1=std::basic_string<char,std::char_traits<char>,std::allocator<c
har
>> (__thiscall EntityWrap::* )(std::string),
            T2=boost::python::detail::not_specified,
            T3=boost::python::detail::not_specified
        ]
        C:\Documents and 
Settings\thomasinov\Desktop\boost_1_29_0\boost\python\c
lass.hpp(227) : see reference to function template 
instantiation 'void boost::py
thon::class_<T,X1,X2,X3>::dispatch_def(const void *,const char 
*,std::basic_stri
ng<_Elem,_Traits,_Ax> ,std::basic_string<_Elem,_Traits,_Ax> 
& )' being compiled
        with
        [
            T=Entity,
            X1=EntityWrap,
            X2=boost::python::detail::not_specified,
            X3=boost::python::detail::not_specified,
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Ax=std::allocator<char>
        ]
        hello.cpp(141) : see reference to function template 
instantiation 'boost
::python::class_<T,X1,X2,X3>::self 
&boost::python::class_<T,X1,X2,X3>::def(const
 char 
*,std::basic_string<_Elem,_Traits,_Ax> ,std::basic_string<_Elem,
_Traits,_A
x> & )' being compiled
        with
        [
            T=Entity,
            X1=EntityWrap,
            X2=boost::python::detail::not_specified,
            X3=boost::python::detail::not_specified,
            _Elem=char,
            _Traits=std::char_traits<char>,
            _Ax=std::allocator<char>
        ]

comment:3 by Joel de Guzman, 20 years ago

Logged In: YES 
user_id=237817

Hi Vince,

Yes, the solution requires the latest CVS code which is
almost Boost 1.30.0 in just a few more days. Pardon me for
not mentioning that. However, there is actually a 1.29.0
solution. See 
http://article.gmane.org/gmane.comp.python.c++/1861 for details.

Cheers,
--
Joel de Guzman
joel at boost-consulting.com
http://www.boost-consulting.com
http://spirit.sf.net

comment:4 by david_abrahams, 16 years ago

Status: assignedclosed
Note: See TracTickets for help on using tickets.