IntWrapper.hpp in indexing_suite
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Firstly, let me confirm I have this right: IntWrapper is converted into an int by bpl using the operator boost::shared_ptr<IntWrapper>() const yes? Or is it the implicitly_convertible? If it is the latter, why do we need the shared_ptr<> operator? Anyway I came up with the below by basically following the testlinear.cpp example but unfortunately it complains "TypeError: No to_python (by-value) converter found for C++ type: class boost::python::indexing::iterator_pair<class CArrayMember<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > *>". The code I used was:
from TestCArrays import * a=MyList() b=a.getData()
I don't get why it's failing to find the conversion with mine whereas it works with the test :( Any help much appreciated. Cheers, Niall - --- cut --- #include <boost/python.hpp> #include <boost/python/suite/indexing/container_suite.hpp> #include <boost/python/suite/indexing/iterator_pair.hpp> #include <boost/python/suite/indexing/container_proxy.hpp> #include <string> class MyList { std::string *array; public: MyList(); std::string *getData() { return array; } }; MyList::MyList() { static std::string data[]={ std::string("Niall"), std::string("is"), std::string("a"), std::string("teapot") }; arrayÚta; } template<class T> class CArrayMember : public T { public: CArrayMember() : T() { } CArrayMember(const CArrayMember &o) : T(o) { } explicit CArrayMember(const T &o) : T(o) { } operator boost::shared_ptr<CArrayMember>() const { return boost::shared_ptr<CArrayMember>(new CArrayMember(*this)); } }; typedef boost::python::indexing::iterator_pair<CArrayMember<std::string> *> MyListCArray; MyListCArray MyList_getArray(MyList &l) { CArrayMember<std::string> *data=static_cast<CArrayMember<std::string> *>(l.getData()); return boost::python::indexing::iterator_pair<CArrayMember<std::string> *> (data, data+100000); // Issue if l is near the 4Gb mark } BOOST_PYTHON_MODULE(TestCArrays) { boost::python::implicitly_convertible<std::string, CArrayMember<std::string> >(); boost::python::class_< MyListCArray, boost::noncopyable>("MyListCArray", boost::python::init<CArrayMember<std::string> *, CArrayMember<std::string> *>()) .def(boost::python::indexing::container_suite< MyListCArray >()); boost::python::class_< MyList, boost::noncopyable >("MyList") .def("getData", &MyList_getArray) ; } -----BEGIN PGP SIGNATURE----- Version: idw's PGP-Frontend 4.9.6.1 / 9-2003 + PGP 8.0.2 iQA/AwUBP4d9/8EcvDLFGKbPEQLYxwCfSIKfJ6mtqNtS76pbNYxTF4OvZZUAoL+3 4O1w8HK0sgRIaFPbxZ5WXbfV =d8TG -----END PGP SIGNATURE-----
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
Firstly, let me confirm I have this right:
IntWrapper is converted into an int by bpl using the operator boost::shared_ptr<IntWrapper>() const yes? Or is it the implicitly_convertible? If it is the latter, why do we need the shared_ptr<> operator?
I've used IntWrapper in a number of different test cases, and ISTR one of them needed a conversion to shared pointer. I suspect that test didn't make it into the CVS, so the shared pointer conversion is currently redundant.
Anyway I came up with the below by basically following the testlinear.cpp example but unfortunately it complains "TypeError: No to_python (by-value) converter found for C++ type: class boost::python::indexing::iterator_pair<class CArrayMember<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > *>". The code I used was:
from TestCArrays import * a=MyList() b=a.getData()
I don't get why it's failing to find the conversion with mine whereas it works with the test :(
I don't think the shared pointer conversion has anything to do with this. The really important bit is this: boost::python::class_<IntWrapper> ("IntWrapper", boost::python::init<int>()) .def ("increment", &IntWrapper::increment) .def ("__repr__", repr) .def ("__cmp__", compare) ; which turns IntWrapper into a Python class, with enough support to convert it to a string and compare two IntWrapper objects. There is also an implicit conversion from int, which means that code like this will work:
v.append (IntWrapper(43)) # OK v.append (44) # Also OK - implicit conversion
Any help much appreciated.
Cheers, Niall
--- cut --- #include <boost/python.hpp> #include <boost/python/suite/indexing/container_suite.hpp> #include <boost/python/suite/indexing/iterator_pair.hpp> #include <boost/python/suite/indexing/container_proxy.hpp> #include <string>
class MyList { std::string *array; public: MyList(); std::string *getData() { return array; } };
MyList::MyList() { static std::string data[]={ std::string("Niall"), std::string("is"), std::string("a"), std::string("teapot") }; arrayÚta; }
template<class T> class CArrayMember : public T { public: CArrayMember() : T() { } CArrayMember(const CArrayMember &o) : T(o) { } explicit CArrayMember(const T &o) : T(o) { } operator boost::shared_ptr<CArrayMember>() const { return boost::shared_ptr<CArrayMember>(new CArrayMember(*this)); } };
typedef boost::python::indexing::iterator_pair<CArrayMember<std::string> *> MyListCArray;
MyListCArray MyList_getArray(MyList &l) { CArrayMember<std::string> *data=static_cast<CArrayMember<std::string> *>(l.getData());
Wow. This is almost certainly undefined behaviour.
return boost::python::indexing::iterator_pair<CArrayMember<std::string> *> (data, data+100000); // Issue if l is near the 4Gb mark }
The bounds checking code will still be compiled, of course, so lying about the bound doesn't actually gain you anything (unless you have no way of figuring out the real bound). You should be able to provide the correct bound without too much trouble in this example.
BOOST_PYTHON_MODULE(TestCArrays) { boost::python::implicitly_convertible<std::string, CArrayMember<std::string> >();
boost::python::class_< MyListCArray, boost::noncopyable>("MyListCArray", boost::python::init<CArrayMember<std::string> *, CArrayMember<std::string> *>()) .def(boost::python::indexing::container_suite< MyListCArray >());
boost::python::class_< MyList, boost::noncopyable >("MyList") .def("getData", &MyList_getArray) ; }
OK, so CArrayMember<std::string> is-a std::string. You still haven't exposed it to Python though. You will need something like this: class_<CArrayMember<std::string>, bases<std::string> > ("CArrayMember_string"); I don't know exactly what else you'll have to do here - maybe define implicit conversions to/from std::string, maybe make it no_init. The container suite doesn't attempt to expose the container's value type to Python - it assumes that you will do that yourself. I would suggest first trying to get CArrayMember<...> working in Python before trying to expose a container of CArrayMembers. -- Raoul Gough. (setq dabbrev-case-fold-search nil)
On 11 Oct 2003 at 12:14, Raoul Gough wrote:
I've used IntWrapper in a number of different test cases, and ISTR one of them needed a conversion to shared pointer. I suspect that test didn't make it into the CVS, so the shared pointer conversion is currently redundant.
Right, sorry, as I mentioned before I don't quite get how all this works ...
I don't think the shared pointer conversion has anything to do with this. The really important bit is this:
boost::python::class_<IntWrapper> ("IntWrapper", boost::python::init<int>()) .def ("increment", &IntWrapper::increment) .def ("__repr__", repr) .def ("__cmp__", compare) ;
which turns IntWrapper into a Python class, with enough support to convert it to a string and compare two IntWrapper objects. There is also an implicit conversion from int, which means that code like this will work:
Hang on, back up a way bit. Firstly, you appear to be implying that the sequence in python is actually of IntWrapper's, not real int's. Actually, now that looks obvious, but I hadn't realised that before. Therefore the down conversion from an IntWrapper to an int is happening when python pulls a value /from/ the sequence eg; repr(sequence). Ok, I now get why there's the __cmp__ and __repr__. But why the increment()? Surely that should be __inc__ or something? (I can't find __inc__ anywhere in the python docs. But it can't be a reference count as there's no way of reading it, decrementing it and besides, it's incrementing the value, not a refCount). Actually, how the hell is bpl converting an IntWrapper to an int? I understand how int becomes IntWrapper (the explicit copy constructor), but not vice versa!
CArrayMember<std::string> *data=static_cast<CArrayMember<std::string> *>(l.getData());
Wow. This is almost certainly undefined behaviour.
Might be according to the standard, but if CArrayMember<> contains no data members, there's no chance of copy slicing and thus no problem.
return boost::python::indexing::iterator_pair<CArrayMember<std::string> *> (data, data+100000); // Issue if l is near the 4Gb mark }
The bounds checking code will still be compiled, of course, so lying about the bound doesn't actually gain you anything (unless you have no way of figuring out the real bound). You should be able to provide the correct bound without too much trouble in this example.
Yeah twas temporary code I'll fix later. I was going to have a template parameter take an address to a function which when called, returns the length of the array (seeing as that function varies between classes). And a template specialisation taking arrays with length attached.
OK, so CArrayMember<std::string> is-a std::string. You still haven't exposed it to Python though. You will need something like this:
class_<CArrayMember<std::string>, bases<std::string> > ("CArrayMember_string");
I don't know exactly what else you'll have to do here - maybe define implicit conversions to/from std::string, maybe make it no_init. The container suite doesn't attempt to expose the container's value type to Python - it assumes that you will do that yourself. I would suggest first trying to get CArrayMember<...> working in Python before trying to expose a container of CArrayMembers.
I had been assuming that std::string already has a registered converter converting it to a python string. But what's *very* important to understand here is that soon I'll make the std::string a template parameter as I want it to be generically applicable ie; you can wrap any C++ object with the necessary gumph to make it a member of a sequence. Ideally however I'd like to somehow divorce the conversion of the types in the array from the accessing of the array. The conversions are already registered and mapped to python types - my CArray code should merely index the array and return a type (often pointer to type) to the array member which then BPL converts as it would normally do (when returned from a method) to its corresponding python type. Cheers, Niall
Imagine we have this C++ class: class CClassActor { private: int m_iValue; public: CClassActor() { m_iValue = 0; } virtual ~CClassActor() {} void SetValue( int iValue ) { m_iValue = iValue; } int GetValue() { return m_iValue; } virtual int GetConcreteInt(); }; and we have a derived class from the first: class CClassActorDerived: CClassActor { private: int m_iOther; CClassActorDerived() { CClassActor(); m_iOther = 0; } virtual ~CClassActorDerived() {} int GetConcreteInt() { return 10; } } As you can see, CClassActor is virtual, but has a constructor, because it initializes some of the attributes of the virtual class that the derived class will eventually use. But what I want is having a class the same way as CClassActorDerived but in Python. As CClassActor is virtual, because it have virtual functions, it is supposed that we need a Wrapper class. What I did whas this: class CClassActorWrap: public CClassActor { public: CClassActorWrap(PyObject* self_) : self(self_) {} int GetConcreteInt() { return py::call_method<int>( self, "GetConcreteInt"); } public: PyObject* self; }; And the module that holds de class wrap: BOOST_PYTHON_MODULE(ClassActorWrapper) { py::class_<CClassActor, CClassActorWrap, boost::noncopyable>( "CppClassActor" ) .def ( "SetValue", &CClassActor::SetValue ) .def ( "GetValue", &CClassActor::GetValue ) ; } Lately in the program I make: PyImport_AppendInittab("ClassActorWrapper", initClassActorWrapper); Py_Initialize(); PyObject* mainmod = PyImport_AddModule("__main__"); ns = PyModule_GetDict(mainmod); Py_INCREF(ns); PyObject* x = PyRun_String( "from ClassActorWrapper import *\n" "class ClassActorDerived(CppClassActor):\n" " def __init__(self):\n" " pass\n" " def GetConcreteInt(self):\n" " print \"Class Python ClassActorDerived deriveda from C++ class CClassActor\"\n" " return 1\n" "class ClassActorDerivedB(CppClassActor):\n" " def __init__(self):\n" " pass\n" " def GetConcreteInt(self):\n" " print \"Class Python ClassActorDerivedB derive from C++ class CClassActor\"\n" " return 2\n" ,Py_file_input, ns, ns); Py_XDECREF(x); PyRun_SimpleString( "ca2 = ClassActorDerived()" ); PyRun_SimpleString( "print ca2.GetConcreteInt()" ); // // --> It ouputs // "Class Python ClassActorDerived deriveda from C++ class CClassActor // 1" // So it is OK PyRun_SimpleString( "ca2b = ClassActorDerivedB()" ); PyRun_SimpleString( "print ca2b.GetConcreteInt()" ); // // --> It ouputs // "Class Python ClassActorDerived deriveda from C++ class CClassActor // 2" // So it is OK PyRun_SimpleString( "print ca2.GetValue()" ); It raises an exception: // TypeError: no to_python (by value) converter found for C++ type: char I do not understand how can I access to de GetValue and SetValue concrete functions of the virtual class CClassActor without rising that exception. And I would like also to call to __init__ of CClassActor inside the __init__ method of the Python Derived classes so that m_iValue gets initialized, but when I define py::class_<CClassActor, CClassActorWrap>( "CppClassActor", py::init<>() ) instead of py::class_<CClassActor, CClassActorWrap, boost::noncopyable>( "CppClassActor", py::no_init ) I get this compile error: e:\...\value_holder.hpp(137) : error C2661: 'CClassActorWrap::CClassActorWrap' : no overloaded function takes 2 parameters e:\...\\make_instance.hpp(69) : see reference to function template instantiation '__thiscall boost::python::objects::value_holder_back_reference<class CClassActor,class CClassActorWrap>:: So, is it possible to do what I want to do? And how can I do it? -Wrap a virtual C++ class with Constructor to Python -Wrap a virtual C++ class with Concrete methods -Make a Python class that derives from that virtual class and can call __init__ of the parent class inside its __init__ method Thanks in advance. David. PD.- using MSVC 6 with Python 2.3 and Boost.python 1.30
I'm sorry that it took so long to respond to this posting. It's probably too late, but. yakumoklesk@yahoo.es writes:
Imagine we have this C++ class:
class CClassActor { private: int m_iValue; public: CClassActor() { m_iValue = 0; } virtual ~CClassActor() {} void SetValue( int iValue ) { m_iValue = iValue; } int GetValue() { return m_iValue; } virtual int GetConcreteInt(); };
and we have a derived class from the first:
class CClassActorDerived: CClassActor { private: int m_iOther; CClassActorDerived() { CClassActor(); m_iOther = 0; } virtual ~CClassActorDerived() {} int GetConcreteInt() { return 10; } }
As you can see, CClassActor is virtual
There's no such thing as a "virtual class" in C++. There are "virtual bases", but you're not using virtual inheritance. You must mean that CClassActor is polymorphic?
but has a constructor, because it initializes some of the attributes of the virtual class that the derived class will eventually use.
But what I want is having a class the same way as CClassActorDerived but in Python. As CClassActor is virtual, because it have virtual functions, it is supposed that we need a Wrapper class. What I did whas this:
<snip lots of code> This error doesn't seem to have any relationship to the code you posted. Furthermore, when I correct all the compilation errors in that code (see enclosed), it runs fine against the current CVS.
And I would like also to call to __init__ of CClassActor inside the __init__ method of the Python Derived classes so that m_iValue gets initialized, but when I define
py::class_<CClassActor, CClassActorWrap>( "CppClassActor", py::init<>() )
instead of
py::class_<CClassActor, CClassActorWrap, boost::noncopyable>( "CppClassActor", py::no_init )
I get this compile error:
e:\...\value_holder.hpp(137) : error C2661: 'CClassActorWrap::CClassActorWrap' : no overloaded function takes 2 parameters e:\...\\make_instance.hpp(69) : see reference to function template instantiation '__thiscall boost::python::objects::value_holder_back_reference<class CClassActor,class CClassActorWrap>::
The code you posted was: py::class_<CClassActor, CClassActorWrap, boost::noncopyable>( "CppClassActor" ) Which is equivalent to: py::class_<CClassActor, CClassActorWrap, boost::noncopyable>( "CppClassActor", py::init<>() ) and it works fine against the current CVS -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 11 Oct 2003 at 19:45, Niall Douglas wrote: Fixed it! The below works lovely, and my thanks for the advice. The problem was the boost::noncopyable in the MyListCArray declaration. Ok, next question: How do you determine the return type of a function pointer? It must be possible as bpl determines return type in def() - get_signature() from signature.hpp looked hopeful, but it appears to be a run-time not compile-time function. To clarify: std::string *foo(); typedef result_of<&foo>::value return_type; .... and return_type is std::string *. Cheers, Niall --- cut --- #include <boost/python.hpp> #include <boost/python/suite/indexing/container_suite.hpp> #include <boost/python/suite/indexing/iterator_pair.hpp> #include <boost/python/suite/indexing/container_proxy.hpp> #include <string> class MyList { std::string *array; public: MyList(); std::string *getData() { return array; } int getDataLen() const { return 4; } }; MyList::MyList() { static std::string data[]={ std::string("Niall"), std::string("is"), std::string("a"), std::string("teapot") }; array=data; } // ****** Where the CArray.h stuff begins ******** template<class T> class CArray { // Sets subType::value to the type of the array's contents template<class sT> struct subType { typedef sT value; }; template<class sT> struct subType<sT *> { typedef sT value; }; //! The type the array contains typedef typename subType<T>::value memberType; public: //! Type used to access array (and sets its upper and lower members) typedef boost::python::indexing::iterator_pair<memberType *> mTypeIt; //! Returns an appropriately wrapped array typename CArray<T>::mTypeIt getArray(MyList &l) { CArray<T>::memberType *data=l.getData(); return CArray<T>::mTypeIt(data, data+1000); // Issue if l is near the 4Gb mark } }; BOOST_PYTHON_MODULE(TestCArrays) { boost::python::class_< CArray<std::string *>::mTypeIt
("CArray_string", boost::python::init<std::string *, std::string *>()) .def(boost::python::indexing::container_suite< CArray<std::string *>::mTypeIt >());
boost::python::class_< MyList, boost::noncopyable >("MyList") .def("getData", &getArray<std::string *>) ; }
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
On 11 Oct 2003 at 19:45, Niall Douglas wrote:
Fixed it!
The below works lovely, and my thanks for the advice. The problem was the boost::noncopyable in the MyListCArray declaration.
I wouldn't have guessed that it had to do with the noncopyable, so I don't think I actually helped out at all. Still, a good result in the end :-)
Ok, next question: How do you determine the return type of a function pointer? It must be possible as bpl determines return type in def() - get_signature() from signature.hpp looked hopeful, but it appears to be a run-time not compile-time function.
To clarify:
std::string *foo();
typedef result_of<&foo>::value return_type;
.... and return_type is std::string *.
Not sure if this is how Boost.Python does it, but the Boost type_traits library would be sufficient for what you described above. See the "Function Traits" section of libs/type_traits/index.htm for details. -- Raoul Gough. (setq dabbrev-case-fold-search nil)
On 12 Oct 2003 at 18:05, Raoul Gough wrote:
The below works lovely, and my thanks for the advice. The problem was the boost::noncopyable in the MyListCArray declaration.
I wouldn't have guessed that it had to do with the noncopyable, so I don't think I actually helped out at all. Still, a good result in the end :-)
Well I had reached that point of despair when you just stare at the code and hope something comes to you. That did, and hey presto!
Ok, next question: How do you determine the return type of a function pointer? It must be possible as bpl determines return type in def() - get_signature() from signature.hpp looked hopeful, but it appears to be a run-time not compile-time function.
Not sure if this is how Boost.Python does it, but the Boost type_traits library would be sufficient for what you described above. See the "Function Traits" section of libs/type_traits/index.htm for details.
Actually this came to me as a sort of epiphany today as I was hauling rigging off a boat - some number of hours later and I have the below, the very first working proof of concept. I should also add that the below is my VERY FIRST C++ metaprogram. I failed a job interview for not knowing it, bought Andrei's Modern C++ Design immediately afterwards and have been playing with it on & off since for the last four months. But not until today did I actually ever write something on my own bat not copied from elsewhere. I feel like my very first 1000 line program in BBC Basic nearly two decades ago now! Note the below is nasty (especially my choice of non-distinctive naming) and only supports one instance currently. I'm going to need a functor more generic than the boost one in order to store a hash table of functions from which I can correctly invoke translation (you can see the hack I used below to access the function pointers). Thus chances are that my end effort will be compatible with my library, but not yours. Thoughts on the below? Is it worth you integrating similar functionality and thus all other bpl users can use it? Or is this too esoteric and/or demanding to warrant inclusion? Cheers, Niall --- cut --- #include <boost/python.hpp> #include <boost/python/suite/indexing/container_suite.hpp> #include <boost/python/suite/indexing/iterator_pair.hpp> #include <boost/python/suite/indexing/container_proxy.hpp> #include <string> class MyList { std::string *array; public: MyList(); std::string *getData() { return array; } int getDataLen() const { return 4; } }; MyList::MyList() { static std::string data[]={ std::string("Niall"), std::string("is"), std::string("a"), std::string("teapot") }; array=data; } // ****** Where the CArray.h stuff begins ******** static void *carray; template<typename getArrayFn, typename getLenFn> class CArray { // Gets the return type of getArrayFn template<typename fn> struct fnInfo; template<typename R, typename O> struct fnInfo<R (O::*)()> { typedef R resultType; typedef O objectType; }; typedef typename fnInfo<getArrayFn> arrayFnInfo; typedef typename arrayFnInfo::resultType arrayType; // Sets subType::value to the type of the array's contents (ie; arrayType less an indirection level) template<class sT> struct subType; template<class sT> struct subType<sT *> { typedef sT value; }; getArrayFn getArrayFunc; // Gets the array data getLenFn getLenFunc; // Gets the length of the array data public: //! The type of the container typedef typename arrayFnInfo::objectType container; //! The type the array contains typedef typename subType<arrayType>::value memberType; //! Type used to access array (and sets its upper and lower members) typedef boost::python::indexing::iterator_pair<memberType *> mTypeIt; //! Constructor CArray(getArrayFn _getArrayFunc, getLenFn _getLenFunc) : getArrayFunc(_getArrayFunc), getLenFunc(_getLenFunc) { boost::python::class_<mTypeIt>("CArray_unique", boost::python::init<memberType *, memberType *>()) .def(boost::python::indexing::container_suite<mTypeIt>()); } //! Returns an appropriately wrapped array static typename mTypeIt getArray(container &l) { CArray<getArrayFn, getLenFn> *that=(CArray<getArrayFn, getLenFn> *) carray; // Temporary memberType *data=(l.*that->getArrayFunc)(); return mTypeIt(data, data+(l.*that->getLenFunc)()); } //! A type representing the getArray function above typedef typename mTypeIt (*getArrayFunctionType)(container &); }; template<typename getArrayFn, typename getLenFn> typename CArray<getArrayFn, getLenFn>::getArrayFunctionType MakeCArray(getArrayFn a, getLenFn b) { carray=(void *) new CArray<getArrayFn, getLenFn>(a, b); return &CArray<getArrayFn, getLenFn>::getArray; } BOOST_PYTHON_MODULE(TestCArrays) { boost::python::class_< MyList, boost::noncopyable >("MyList") .def("getData", MakeCArray(&MyList::getData, &MyList::getDataLen)) ; }
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
On 12 Oct 2003 at 18:05, Raoul Gough wrote: [snip]
Not sure if this is how Boost.Python does it, but the Boost type_traits library would be sufficient for what you described above. See the "Function Traits" section of libs/type_traits/index.htm for details.
Actually this came to me as a sort of epiphany today as I was hauling rigging off a boat - some number of hours later and I have the below, the very first working proof of concept.
I should also add that the below is my VERY FIRST C++ metaprogram. I failed a job interview for not knowing it, bought Andrei's Modern C++ Design immediately afterwards and have been playing with it on & off since for the last four months. But not until today did I actually ever write something on my own bat not copied from elsewhere. I feel like my very first 1000 line program in BBC Basic nearly two decades ago now!
Note the below is nasty (especially my choice of non-distinctive naming) and only supports one instance currently. I'm going to need a functor more generic than the boost one in order to store a hash table of functions from which I can correctly invoke translation (you can see the hack I used below to access the function pointers). Thus chances are that my end effort will be compatible with my library, but not yours.
Thoughts on the below? Is it worth you integrating similar functionality and thus all other bpl users can use it? Or is this too esoteric and/or demanding to warrant inclusion? [snip]
I can't really comment on this, because I don't understand what the code is supposed to do. Can you explain the motivation for it? I have the feeling that you may be reinventing one or two wheels as well - e.g. have you considered using std::vector or boost::array, and what about type_traits? Also, I wonder about the use of member function pointers - is there an easier way to get what you want?
static void *carray; ... carray=(void *) new CArray<getArrayFn, getLenFn>(a, b);
This look pretty suspect to me - what's going on here? -- Raoul Gough. (setq dabbrev-case-fold-search nil)
On 13 Oct 2003 at 10:01, Raoul Gough wrote:
Thoughts on the below? Is it worth you integrating similar functionality and thus all other bpl users can use it? Or is this too esoteric and/or demanding to warrant inclusion? [snip]
I can't really comment on this, because I don't understand what the code is supposed to do. Can you explain the motivation for it?
The motivation is with a single call to wrap C arrays. Let me give you an example: I have a class FXObjectList which is a bespoke container for FXObject's. Among the many fun things it does which normal containers would not it also returns the container's contents as a C array and provides another method returning the length. Let's call them FXObject *getData() and int getDataLen(). Now when wrapping FXObjectList I already have FXObject wrapped - it does it's own thing and there's no problem. Just FXObjectList. Before I could not wrap FXObjectList without lots of custom code whereas with code like I previously posted, it becomes as simple as: boost::python::class_<FXObjectList, boost::noncopyable>("FXObjectList") .def("getData", MakeCArray(&FXObjectList::getData, &FXObjectList::getDataLen)) And bam!, in one fell swoop the problem is solved. Another problem I have is that the library I am wrapping provides static const indexed lists of constant strings (providing stuff like DND target names and MIME types). It's very hard to provide these to python code but again with the above, bam! and the problem is solved. Lastly I have some code which unfortunately returns a pointer to a single value just so it can indicate "value is invalid" by returning a null pointer. In this situation a MakeCArray(&function, 1) means once again bam!, and the problem is solved.
I have the feeling that you may be reinventing one or two wheels as well - e.g. have you considered using std::vector or boost::array, and what about type_traits?
I'm wrapping a large preexisting library which is not my own. It uses many idioms which make translation into python hard which has up until now made python wrappings for the library very tough to maintain.
Also, I wonder about the use of member function pointers - is there an easier way to get what you want?
Not at all. Those member functions are the official interface for accessing the array's contents. They must be called at run-time to yield correct access.
static void *carray; ... carray=(void *) new CArray<getArrayFn, getLenFn>(a, b);
This look pretty suspect to me - what's going on here?
More temporary code bootstrapping in static data. The actual implementation will use a generic functor class I'm writing to store the function pointers in a global list which is hash-indexed. As I previously mentioned, it was a proof of concept. Now I've explained, your thoughts on whether it would be of use to others? Cheers, Niall
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
boost::python::class_<FXObjectList, boost::noncopyable>("FXObjectList") .def("getData", MakeCArray(&FXObjectList::getData, &FXObjectList::getDataLen))
And bam!, in one fell swoop the problem is solved.
I'm not sure I know exactly what problem you're solving (what do you expect to be able to do with the result of this getData call?) but your use of a global pointer to hold the result is terribly unsafe... not to mention the fact that your code leaks CArray objects like a sieve. If you're satisfied with that, I guess I'm satisfied. Otherwise, perhaps you'd like to post a little more detail about how you want to be able to *use* this code from Python? I think people here, especially Raoul, should be able to make some suggestions which would solve the problem in a more principled way. -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 13 Oct 2003 at 19:12, David Abrahams wrote:
boost::python::class_<FXObjectList, boost::noncopyable>("FXObjectList") .def("getData", MakeCArray(&FXObjectList::getData, &FXObjectList::getDataLen))
And bam!, in one fell swoop the problem is solved.
I'm not sure I know exactly what problem you're solving (what do you expect to be able to do with the result of this getData call?) but your use of a global pointer to hold the result is terribly unsafe... not to mention the fact that your code leaks CArray objects like a sieve.
Again, all artifacts of proof of concept code. The hard part for me was the compile-time programming. I should add that in all the code I've written in the last three years, I've had a total of I think eight memory leaks ever (according to valgrind and MSVC's debug CRT memory leak facilities). And that's over 50,000 lines now, probably more.
If you're satisfied with that, I guess I'm satisfied. Otherwise, perhaps you'd like to post a little more detail about how you want to be able to *use* this code from Python? I think people here, especially Raoul, should be able to make some suggestions which would solve the problem in a more principled way.
from TestCArrays import * a=MyList() b=a.getData() print len(b) 4 print b[0:4] ['Niall', 'is', 'a', 'teapot'] b[1]="is not" print b[0:4] ['Niall', 'is not', 'a', 'teapot']
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code. Cheers, Niall
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
If you're satisfied with that, I guess I'm satisfied. Otherwise, perhaps you'd like to post a little more detail about how you want to be able to *use* this code from Python? I think people here, especially Raoul, should be able to make some suggestions which would solve the problem in a more principled way.
from TestCArrays import * a=MyList() b=a.getData() print len(b) 4 print b[0:4] ['Niall', 'is', 'a', 'teapot'] b[1]="is not" print b[0:4] ['Niall', 'is not', 'a', 'teapot']
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code.
I'm not sure what "munge code" means. The behavior you show above is almost trivial to achieve using Boost.Python, without anything I consider to be "munge code". Question 1: when you modify b above, do you expect it to affect a? Question 2: do you expect b to be an actual Python list, i.e. type(b) == list ?? -- Dave Abrahams Boost Consulting www.boost-consulting.com
On 14 Oct 2003 at 7:42, David Abrahams wrote:
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code.
I'm not sure what "munge code" means. The behavior you show above is almost trivial to achieve using Boost.Python, without anything I consider to be "munge code".
I'm getting that picture from Raoul. However if it's so easy, why is Raoul developing indexing_suite?
Question 1: when you modify b above, do you expect it to affect a?
Yes.
Question 2: do you expect b to be an actual Python list, i.e. type(b) == list ??
Not really as it can't be extended in length. So while it looks like a list and acts like a list, it's not a list. (Don't worry, the users will know this. And remember this affects about seven different areas of the library which I've never used so basically I'm providing a default action for users so they can tell me afterwards if they don't like it). Cheers, Niall
Niall Douglas wrote:
On 14 Oct 2003 at 7:42, David Abrahams wrote:
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code.
I'm not sure what "munge code" means. The behavior you show above is almost trivial to achieve using Boost.Python, without anything I consider to be "munge code".
I'm getting that picture from Raoul. However if it's so easy, why is Raoul developing indexing_suite?
To handle reference and lifetime issues which are mostly not relevant to you because your container holds strings, which are immutable in Python.
Question 1: when you modify b above, do you expect it to affect a?
Yes.
Question 2: do you expect b to be an actual Python list, i.e. type(b) == list ??
Not really as it can't be extended in length. So while it looks like a list and acts like a list, it's not a list.
In that case, I suggest you just make a getData function which returns self. That'll make your life *way* simpler.
On 15 Oct 2003 at 11:38, David Abrahams wrote:
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code.
I'm not sure what "munge code" means. The behavior you show above is almost trivial to achieve using Boost.Python, without anything I consider to be "munge code".
I'm getting that picture from Raoul. However if it's so easy, why is Raoul developing indexing_suite?
To handle reference and lifetime issues which are mostly not relevant to you because your container holds strings, which are immutable in Python.
?!?!?!? Whatever gave you the idea my container holds strings? That was my *test* container and I chose strings because BPL already provides conversion to python types. The real actual world use is for arbitrary bespoke containers returning arbitrary objects sometimes by value and sometimes by reference. I did state that the code I posted was proof of concept many times! Cheers, Niall
Niall Douglas wrote:
On 15 Oct 2003 at 11:38, David Abrahams wrote:
The key here is that MyList knows nothing of python and offers no more than a C array of pointers, something which up until now was hard to wrap without lots of munge code.
I'm not sure what "munge code" means. The behavior you show above is almost trivial to achieve using Boost.Python, without anything I consider to be "munge code".
I'm getting that picture from Raoul. However if it's so easy, why is Raoul developing indexing_suite?
To handle reference and lifetime issues which are mostly not relevant to you because your container holds strings, which are immutable in Python.
?!?!?!?
Whatever gave you the idea my container holds strings?
Your examples did. You can't really claim to be shocked, can you?
That was my *test* container and I chose strings because BPL already provides conversion to python types.
The real actual world use is for arbitrary bespoke containers
Hmm... bespoke Bespoke (pronounced bee-SPOHK) is a term used in the United Kingdom and elsewhere for an individually- or custom-made product or service. Traditionally applied to custom-tailored clothing, the term has been extended to information technology, especially for software consulting services. Typically, software consulting companies offer packaged (already invented and generally applicable) software and bespoke software for client needs that can't be satisfied by packaged software. In the U.S., bespoke software is often called custom or custom-designed software OK.
returning arbitrary objects sometimes by value and sometimes by reference.
OK, then I'd use the suites if I were you.
I did state that the code I posted was proof of concept many times!
I don't know why you're telling me that, but anyway... I was just trying to help you get to an easy and reliable solution. If you're interested in pursuing your own homegrown solution, it's fine by me. I'll be happy to go back to my other pursuits and stop pointing out that I think there's a better way. Regards, Dave
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
On 13 Oct 2003 at 10:01, Raoul Gough wrote:
Thoughts on the below? Is it worth you integrating similar functionality and thus all other bpl users can use it? Or is this too esoteric and/or demanding to warrant inclusion? [snip]
I can't really comment on this, because I don't understand what the code is supposed to do. Can you explain the motivation for it?
The motivation is with a single call to wrap C arrays. Let me give you an example:
I have a class FXObjectList which is a bespoke container for FXObject's. Among the many fun things it does which normal containers would not it also returns the container's contents as a C array and provides another method returning the length. Let's call them FXObject *getData() and int getDataLen().
Now when wrapping FXObjectList I already have FXObject wrapped - it does it's own thing and there's no problem. Just FXObjectList. Before I could not wrap FXObjectList without lots of custom code whereas with code like I previously posted, it becomes as simple as:
boost::python::class_<FXObjectList, boost::noncopyable>("FXObjectList") .def("getData", MakeCArray(&FXObjectList::getData, &FXObjectList::getDataLen))
And bam!, in one fell swoop the problem is solved.
Another problem I have is that the library I am wrapping provides static const indexed lists of constant strings (providing stuff like DND target names and MIME types). It's very hard to provide these to python code but again with the above, bam! and the problem is solved.
Lastly I have some code which unfortunately returns a pointer to a single value just so it can indicate "value is invalid" by returning a null pointer. In this situation a MakeCArray(&function, 1) means once again bam!, and the problem is solved.
I have the feeling that you may be reinventing one or two wheels as well - e.g. have you considered using std::vector or boost::array, and what about type_traits?
I'm wrapping a large preexisting library which is not my own. It uses many idioms which make translation into python hard which has up until now made python wrappings for the library very tough to maintain.
Now I'm beginning to understand :-) You can't re-use (for instance) boost::array because this library already does things its own way.
Also, I wonder about the use of member function pointers - is there an easier way to get what you want?
Not at all. Those member functions are the official interface for accessing the array's contents. They must be called at run-time to yield correct access.
It was more a question of how you inform your wrapper layer what functions should be called. e.g. you could use a traits class, which would take care of this on a per-type basis (assuming that the functions to call don't vary from one object to another). Or you could just use function overloading, something like the indexing::begin() and indexing::end() functions for arrays.
static void *carray; ... carray=(void *) new CArray<getArrayFn, getLenFn>(a, b);
This look pretty suspect to me - what's going on here?
More temporary code bootstrapping in static data. The actual implementation will use a generic functor class I'm writing to store the function pointers in a global list which is hash-indexed.
As I previously mentioned, it was a proof of concept.
Now I've explained, your thoughts on whether it would be of use to others?
Well, it's starting to make more sense to me. In fact, I think there is already something similar to this which returns a Python iterator instead of an indexing::iterator_pair. IIRC it's called boost::python::range or similar. The iterator_pair (with the container suite) has far more functionality than a Python iterator, of course. To summarise: this boils down to a method for generating an iterator_pair from an arbitrary object which has some way of generating two iterators. Does that sound about right? -- Raoul Gough. (setq dabbrev-case-fold-search nil)
On 14 Oct 2003 at 1:00, Raoul Gough wrote:
I'm wrapping a large preexisting library which is not my own. It uses many idioms which make translation into python hard which has up until now made python wrappings for the library very tough to maintain.
Now I'm beginning to understand :-) You can't re-use (for instance) boost::array because this library already does things its own way.
Precisely. It doesn't even use the STL and its authors have clearly stated that the STL is *not* *permitted* to enter the library. Furthermore while they'll agree to /minor/ changes, they won't go for anything more than trivial. Also while they have python bindings which they use a lot (they like python), they are only for the stable release and not the development release and thus are very out of date. The existing bindings are generated using SWIG and are a real pain to keep up to date - indeed they are sufficiently convoluted that I believe one of the SWIG authors is the only one able to substantially modify them.
Also, I wonder about the use of member function pointers - is there an easier way to get what you want?
Not at all. Those member functions are the official interface for accessing the array's contents. They must be called at run-time to yield correct access.
It was more a question of how you inform your wrapper layer what functions should be called. e.g. you could use a traits class, which would take care of this on a per-type basis (assuming that the functions to call don't vary from one object to another).
Already thought of that and they do vary in name from container to container.
Or you could just use function overloading, something like the indexing::begin() and indexing::end() functions for arrays.
That's interesting, but wouldn't it require a separate declaration of the required functions somewhere beforehand? The solution I chose means very minimal manual editing of pyste's output - in fact, I'm going to make it a diff file applied by scons after calling pyste.
Now I've explained, your thoughts on whether it would be of use to others?
Well, it's starting to make more sense to me. In fact, I think there is already something similar to this which returns a Python iterator instead of an indexing::iterator_pair. IIRC it's called boost::python::range or similar. The iterator_pair (with the container suite) has far more functionality than a Python iterator, of course.
Does boost::python::range provide random access? I don't think it does. Basically, I want to make C arrays available as lists to python. Your suite (and thank you for it) with my little extension appears to do this very nicely indeed. For example, there's now no reason why a bitmap's contents cannot be directly manipulated by python eg; bitmapdata[4]=0xff puts a white pixel at (4,0) on the screen.
To summarise: this boils down to a method for generating an iterator_pair from an arbitrary object which has some way of generating two iterators. Does that sound about right?
Nearly. This boils down to a method for generating an iterator_pair from an arbitrary object providing no more than a pointer and a length. The pointer and length can be retrieved from any arbitrary function except ones which return the value through their parameters (a lambda wrap could fix these though). Once I'm done with the overloads, you won't even need a container object. I have several static global arrays I need to provide to python. Cheers, Niall
Greetings I'm embedding python in order to allow our customers to decide, using a python script, whether certain messages in the system are distributed to other instances of the system. The script must assign 1 or 0 to a variable called SendIt (I'm going for the simplest solution at the moment...) and I read that variable's value after the script finishes. I'd like to know what people do to ensure that the script doesn't enter an infinite loop. If that's not possible, how can one detect that the script is in an infinite loop and stop it. The solution that's been considered so far is to run the interpreter in its own thread and put a limit on its run time. But I figure there's got to be a better way. Thanks for any suggestions. Ben -- ---------------------------------------- (o__ Benjamin Collar //\ GAMIC mbH ++49 (0)241 889 110 V_/_ Developer/System Administrator To know recursion, you must first know recursion
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
On 14 Oct 2003 at 1:00, Raoul Gough wrote: [snip]
Also, I wonder about the use of member function pointers - is there an easier way to get what you want?
Not at all. Those member functions are the official interface for accessing the array's contents. They must be called at run-time to yield correct access.
It was more a question of how you inform your wrapper layer what functions should be called. e.g. you could use a traits class, which would take care of this on a per-type basis (assuming that the functions to call don't vary from one object to another).
Already thought of that and they do vary in name from container to container.
Do you mean they just vary from container type to container type? Or from one instance of a container type to another? For instance, the standard library can use char_traits::eq to compare chars, because the comparison function doesn't vary from one char object to another (just from one *type* of char to another).
Or you could just use function overloading, something like the indexing::begin() and indexing::end() functions for arrays.
That's interesting, but wouldn't it require a separate declaration of the required functions somewhere beforehand? The solution I chose means very minimal manual editing of pyste's output - in fact, I'm going to make it a diff file applied by scons after calling pyste.
Yes it would. However, I would assume that the number of different types of containers within the library is more or less fixed, so you could just put this sort of thing in a separate header: template<typename Element> boost::python::indexing::iterator_pair<Element *> make_iter_pair (container_type_1<Element> &c1) { return boost::python::indexing::iterator_pair<Element *> (c1.getArray(), c1.getArray() + c1.getLength()); } template<typename Element> boost::python::indexing::iterator_pair<Element *> make_iter_pair (container_type_2<Element> &c) { return boost::python::indexing::iterator_pair<Element *> (c2.get_array(), c2.get_array() + c2.get_length()); } Basically, you've somewhere got to say "use getArray here" and "use get_array" there. If that is invariant on the container type, why mess around with storing pointers to member functions in every object? Things would be a bit different if you have a single type that contains two or more different sequences and you want to select from any of them at run-time.
Now I've explained, your thoughts on whether it would be of use to others?
Well, it's starting to make more sense to me. In fact, I think there is already something similar to this which returns a Python iterator instead of an indexing::iterator_pair. IIRC it's called boost::python::range or similar. The iterator_pair (with the container suite) has far more functionality than a Python iterator, of course.
Does boost::python::range provide random access? I don't think it does.
Like I said, the iterator pair/container suite combination provides a lot more functionality, but that doesn't really affect the choice of interface, since the only requirement is somehow to provide an iterator range through it. The range() function accepts pointers to member functions, pointers to normal functions or even pointers to member variables. Just to prove all my ideas ill-founded, it doesn't support traits or function overloading :-)
Basically, I want to make C arrays available as lists to python. Your suite (and thank you for it) with my little extension appears to do this very nicely indeed.
For example, there's now no reason why a bitmap's contents cannot be directly manipulated by python eg; bitmapdata[4]=0xff puts a white pixel at (4,0) on the screen.
Of course, this particular example wouldn't be very fast. That expression results in a whole bunch of Boost.Python code being executed to figure out which C++ functions to call and how to extract the C++ objects from the Python ones. Where you really get performance is if you can do one call from Python to C++ that then performs a whole lot of processing.
To summarise: this boils down to a method for generating an iterator_pair from an arbitrary object which has some way of generating two iterators. Does that sound about right?
Nearly.
This boils down to a method for generating an iterator_pair from an arbitrary object providing no more than a pointer and a length. The pointer and length can be retrieved from any arbitrary function except ones which return the value through their parameters (a lambda wrap could fix these though).
On the other hand, the begin() and end() style is more general. It's also easy enough to write a wrapper for it if you only have begin() and length(), but not so in the other direction. For example, if your container doesn't have random access, using begin() and length() doesn't make sense at all.
Once I'm done with the overloads, you won't even need a container object. I have several static global arrays I need to provide to python.
Maybe something like this would be useful to have in the library? template<typename T, std::size_t N> iterator_pair<T *> make_iterator_pair (T (&array)[N]) { return iterator_pair<T *>(array, array + N); } -- Raoul Gough. (setq dabbrev-case-fold-search nil)
On 14 Oct 2003 at 11:43, Raoul Gough wrote:
Already thought of that and they do vary in name from container to container.
Do you mean they just vary from container type to container type? Or from one instance of a container type to another? For instance, the standard library can use char_traits::eq to compare chars, because the comparison function doesn't vary from one char object to another (just from one *type* of char to another).
No, they're fixed per container.
That's interesting, but wouldn't it require a separate declaration of the required functions somewhere beforehand? The solution I chose means very minimal manual editing of pyste's output - in fact, I'm going to make it a diff file applied by scons after calling pyste.
Yes it would. However, I would assume that the number of different types of containers within the library is more or less fixed, so you could just put this sort of thing in a separate header: [snip]
Basically, you've somewhere got to say "use getArray here" and "use get_array" there. If that is invariant on the container type, why mess around with storing pointers to member functions in every object? Things would be a bit different if you have a single type that contains two or more different sequences and you want to select from any of them at run-time.
Your approach looks better. If that really does work as easily as sticking those little bits of code in, I'll go for that instead.
For example, there's now no reason why a bitmap's contents cannot be directly manipulated by python eg; bitmapdata[4]=0xff puts a white pixel at (4,0) on the screen.
Of course, this particular example wouldn't be very fast. That expression results in a whole bunch of Boost.Python code being executed to figure out which C++ functions to call and how to extract the C++ objects from the Python ones. Where you really get performance is if you can do one call from Python to C++ that then performs a whole lot of processing.
That's cool with me. I just want to provide as full a toolset as possible and let the users decide what's best for them.
Once I'm done with the overloads, you won't even need a container object. I have several static global arrays I need to provide to python.
Maybe something like this would be useful to have in the library?
template<typename T, std::size_t N> iterator_pair<T *> make_iterator_pair (T (&array)[N]) { return iterator_pair<T *>(array, array + N); }
Well I'll find this out tonight, but if that little bit of code like the above is all I need, I'll go for that. Why aren't useful things like that in the docs? What I'm doing is surely a common problem to a lot of people wrapping C++ (ie; wrapping C arrays) - though I'll admit I've never seen anyone ask about it before in the archives. Cheers, Niall
"Niall Douglas" <s_sourceforge@nedprod.com> writes:
On 11 Oct 2003 at 12:14, Raoul Gough wrote:
I've used IntWrapper in a number of different test cases, and ISTR one of them needed a conversion to shared pointer. I suspect that test didn't make it into the CVS, so the shared pointer conversion is currently redundant.
Right, sorry, as I mentioned before I don't quite get how all this works ...
I don't think the shared pointer conversion has anything to do with this. The really important bit is this:
boost::python::class_<IntWrapper> ("IntWrapper", boost::python::init<int>()) .def ("increment", &IntWrapper::increment) .def ("__repr__", repr) .def ("__cmp__", compare) ;
which turns IntWrapper into a Python class, with enough support to convert it to a string and compare two IntWrapper objects. There is also an implicit conversion from int, which means that code like this will work:
Hang on, back up a way bit. Firstly, you appear to be implying that the sequence in python is actually of IntWrapper's, not real int's. Actually, now that looks obvious, but I hadn't realised that before.
Therefore the down conversion from an IntWrapper to an int is happening when python pulls a value /from/ the sequence eg; repr(sequence).
I don't think that Python ever converts the IntWrapper to an int - the __repr__ method returns a string. Although the int member variable is publically visible, AFAIR there isn't any external code that accesses it.
Ok, I now get why there's the __cmp__ and __repr__. But why the increment()? Surely that should be __inc__ or something? (I can't find __inc__ anywhere in the python docs. But it can't be a reference count as there's no way of reading it, decrementing it and besides, it's incrementing the value, not a refCount).
As you surmise, the increment doesn't have anything to do with the reference counting. It's just a minimal mutation function to facilitate checking whether two Python objects refer to the same IntWrapper object or not - the tests typically call obj1.increment() and check whether the values of obj1 and obj2 are still identical after the change. That's really the main reason to have IntWrapper (or int_wrapper as it will soon be called) apart from the optional tracing, which isn't currently used.
Actually, how the hell is bpl converting an IntWrapper to an int? I understand how int becomes IntWrapper (the explicit copy constructor), but not vice versa!
CArrayMember<std::string> *data=static_cast<CArrayMember<std::string> *>(l.getData());
Wow. This is almost certainly undefined behaviour.
Might be according to the standard, but if CArrayMember<> contains no data members, there's no chance of copy slicing and thus no problem.
I guess it would be pretty surprising for the types to have different layouts or sizes if you don't add any more data members. I think the risk exists in theory at least, so other compilers or compiler options might break the code. There is also the (maybe not so theoretical) risk that somebody accidentally adds a member variable to the class template.
return boost::python::indexing::iterator_pair<CArrayMember<std::string> *> (data, data+100000); // Issue if l is near the 4Gb mark }
The bounds checking code will still be compiled, of course, so lying about the bound doesn't actually gain you anything (unless you have no way of figuring out the real bound). You should be able to provide the correct bound without too much trouble in this example.
Yeah twas temporary code I'll fix later. I was going to have a template parameter take an address to a function which when called, returns the length of the array (seeing as that function varies between classes). And a template specialisation taking arrays with length attached.
Ah ha. Now it makes sense.
OK, so CArrayMember<std::string> is-a std::string. You still haven't exposed it to Python though. You will need something like this:
class_<CArrayMember<std::string>, bases<std::string> > ("CArrayMember_string");
I don't know exactly what else you'll have to do here - maybe define implicit conversions to/from std::string, maybe make it no_init. The container suite doesn't attempt to expose the container's value type to Python - it assumes that you will do that yourself. I would suggest first trying to get CArrayMember<...> working in Python before trying to expose a container of CArrayMembers.
I had been assuming that std::string already has a registered converter converting it to a python string.
Yes, it does. I hadn't really thought this through before, but the implicit conversion from derived to base must mean that Boost.Python can already return the string portion of the object to Python. This may result in object slicing if you're returning the object by value, though (I guess not a problem in this case).
But what's *very* important to understand here is that soon I'll make the std::string a template parameter as I want it to be generically applicable ie; you can wrap any C++ object with the necessary gumph to make it a member of a sequence.
Ideally however I'd like to somehow divorce the conversion of the types in the array from the accessing of the array. The conversions are already registered and mapped to python types - my CArray code should merely index the array and return a type (often pointer to type) to the array member which then BPL converts as it would normally do (when returned from a method) to its corresponding python type.
Sounds alright to me. -- Raoul Gough. (setq dabbrev-case-fold-search nil)
participants (5)
-
Benjamin Collar -
David Abrahams -
Niall Douglas -
Raoul Gough -
yakumoklesk@yahoo.es