Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r54746:0585f40cf68b Date: 2012-04-25 12:15 +0200 http://bitbucket.org/pypy/pypy/changeset/0585f40cf68b/
Log: merge heads diff --git a/pypy/doc/cppyy.rst b/pypy/doc/cppyy.rst --- a/pypy/doc/cppyy.rst +++ b/pypy/doc/cppyy.rst @@ -80,7 +80,7 @@ void SetMyInt(int i) { m_myint = i; } public: - int m_myint; + int m_myint; }; Then, generate the bindings using ``genreflex`` (part of ROOT), and compile the @@ -111,6 +111,121 @@ That's all there is to it! +Advanced example +================ +The following snippet of C++ is very contrived, to allow showing that such +pathological code can be handled and to show how certain features play out in +practice:: + + $ cat MyAdvanced.h + #include <string> + + class Base1 { + public: + Base1(int i) : m_i(i) {} + virtual ~Base1() {} + int m_i; + }; + + class Base2 { + public: + Base2(double d) : m_d(d) {} + virtual ~Base2() {} + double m_d; + }; + + class C; + + class Derived : public virtual Base1, public virtual Base2 { + public: + Derived(const std::string& name, int i, double d) : Base1(i), Base2(d), m_name(name) {} + virtual C* gimeC() { return (C*)0; } + std::string m_name; + }; + + Base1* BaseFactory(const std::string& name, int i, double d) { + return new Derived(name, i, d); + } + +This code is still only in a header file, with all functions inline, for +convenience of the example. +If the implementations live in a separate source file or shared library, the +only change needed is to link those in when building the reflection library. + +If you were to run ``genreflex`` like above in the basic example, you will +find that not all classes of interest will be reflected, nor will be the +global factory function. +In particular, ``std::string`` will be missing, since it is not defined in +this header file, but in a header file that is included. +In practical terms, general classes such as ``std::string`` should live in a +core reflection set, but for the moment assume we want to have it in the +reflection library that we are building for this example. + +The ``genreflex`` script can be steered using a so-called `selection file`_, +which is a simple XML file specifying, either explicitly or by using a +pattern, which classes, variables, namespaces, etc. to select from the given +header file. +With the aid of a selection file, a large project can be easily managed: +simply ``#include`` all relevant headers into a single header file that is +handed to ``genreflex``. +Then, apply a selection file to pick up all the relevant classes. +For our purposes, the following rather straightforward selection will do +(the name ``lcgdict`` for the root is historical, but required):: + + $ cat MyAdvanced.xml + <lcgdict> + <class pattern="Base?" /> + <class name="Derived" /> + <class name="std::string" /> + <function name="BaseFactory" /> + </lcgdict> + +.. _`selection file`: http://root.cern.ch/drupal/content/generating-reflex-dictionaries + +Now the reflection info can be generated and compiled:: + + $ genreflex MyAdvanced.h --selection=MyAdvanced.xml + $ g++ -fPIC -rdynamic -O2 -shared -I$ROOTSYS/include MyAdvanced_rflx.cpp -o libAdvExDict.so + +and subsequently be used from PyPy:: + + >>>> import cppyy + >>>> cppyy.load_reflection_info("libAdvExDict.so") + <CPPLibrary object at 0x00007fdb48fc8120> + >>>> d = cppyy.gbl.BaseFactory("name", 42, 3.14) + >>>> type(d) + <class '__main__.Derived'> + >>>> d.m_i + 42 + >>>> d.m_d + 3.14 + >>>> d.m_name == "name" + True + >>>> + +Again, that's all there is to it! + +A couple of things to note, though. +If you look back at the C++ definition of the ``BaseFactory`` function, +you will see that it declares the return type to be a ``Base1``, yet the +bindings return an object of the actual type ``Derived``? +This choice is made for a couple of reasons. +First, it makes method dispatching easier: if bound objects are always their +most derived type, then it is easy to calculate any offsets, if necessary. +Second, it makes memory management easier: the combination of the type and +the memory address uniquely identifies an object. +That way, it can be recycled and object identity can be maintained if it is +entered as a function argument into C++ and comes back to PyPy as a return +value. +Last, but not least, casting is decidedly unpythonistic. +By always providing the most derived type known, casting becomes unnecessary. +For example, the data member of ``Base2`` is simply directly available. +Note also that the unreflected ``gimeC`` method of ``Derived`` does not +preclude its use. +It is only the ``gimeC`` method that is unusable as long as class ``C`` is +unknown to the system. + + Features ======== @@ -160,6 +275,8 @@ * **doc strings**: The doc string of a method or function contains the C++ arguments and return types of all overloads of that name, as applicable. +* **enums**: Are translated as ints with no further checking. + * **functions**: Work as expected and live in their appropriate namespace (which can be the global one, ``cppyy.gbl``). @@ -236,6 +353,9 @@ using classes that themselves are templates (etc.) in the arguments. All classes must already exist in the loaded reflection info. +* **typedefs**: Are simple python references to the actual classes to which + they refer. + * **unary operators**: Are supported if a python equivalent exists, and if the operator is defined in the C++ class. @@ -253,6 +373,107 @@ Only that one specific method can not be used. +Templates +========= + +A bit of special care needs to be taken for the use of templates. +For a templated class to be completely available, it must be guaranteed that +said class is fully instantiated, and hence all executable C++ code is +generated and compiled in. +The easiest way to fulfill that guarantee is by explicit instantiation in the +header file that is handed to ``genreflex``. +The following example should make that clear:: + + $ cat MyTemplate.h + #include <vector> + + class MyClass { + public: + MyClass(int i = -99) : m_i(i) {} + MyClass(const MyClass& s) : m_i(s.m_i) {} + MyClass& operator=(const MyClass& s) { m_i = s.m_i; return *this; } + ~MyClass() {} + int m_i; + }; + + template class std::vector<MyClass>; + +If you know for certain that all symbols will be linked in from other sources, +you can also declare the explicit template instantiation ``extern``. + +Unfortunately, this is not enough for gcc. +The iterators, if they are going to be used, need to be instantiated as well, +as do the comparison operators on those iterators, as these live in an +internal namespace, rather than in the iterator classes. +One way to handle this, is to deal with this once in a macro, then reuse that +macro for all ``vector`` classes. +Thus, the header above needs this, instead of just the explicit instantiation +of the ``vector<MyClass>``:: + + #define STLTYPES_EXPLICIT_INSTANTIATION_DECL(STLTYPE, TTYPE) \ + template class std::STLTYPE< TTYPE >; \ + template class __gnu_cxx::__normal_iterator<TTYPE*, std::STLTYPE< TTYPE > >; \ + template class __gnu_cxx::__normal_iterator<const TTYPE*, std::STLTYPE< TTYPE > >;\ + namespace __gnu_cxx { \ + template bool operator==(const std::STLTYPE< TTYPE >::iterator&, \ + const std::STLTYPE< TTYPE >::iterator&); \ + template bool operator!=(const std::STLTYPE< TTYPE >::iterator&, \ + const std::STLTYPE< TTYPE >::iterator&); \ + } + + STLTYPES_EXPLICIT_INSTANTIATION_DECL(vector, MyClass) + +Then, still for gcc, the selection file needs to contain the full hierarchy as +well as the global overloads for comparisons for the iterators:: + + $ cat MyTemplate.xml + <lcgdict> + <class pattern="std::vector<*>" /> + <class pattern="__gnu_cxx::__normal_iterator<*>" /> + <class pattern="__gnu_cxx::new_allocator<*>" /> + <class pattern="std::_Vector_base<*>" /> + <class pattern="std::_Vector_base<*>::_Vector_impl" /> + <class pattern="std::allocator<*>" /> + <function name="__gnu_cxx::operator=="/> + <function name="__gnu_cxx::operator!="/> + + <class name="MyClass" /> + </lcgdict> + +Run the normal ``genreflex`` and compilation steps:: + + $ genreflex MyTemplate.h --selection=MyTemplate.xm + $ g++ -fPIC -rdynamic -O2 -shared -I$ROOTSYS/include MyTemplate_rflx.cpp -o libTemplateDict.so + +Note: this is a dirty corner that clearly could do with some automation, +even if the macro already helps. +Such automation is planned. +In fact, in the cling world, the backend can perform the template +instantations and generate the reflection info on the fly, and none of the +above will any longer be necessary. + +Subsequent use should be as expected. +Note the meta-class style of "instantiating" the template:: + + >>>> import cppyy + >>>> cppyy.load_reflection_info("libTemplateDict.so") + >>>> std = cppyy.gbl.std + >>>> MyClass = cppyy.gbl.MyClass + >>>> v = std.vector(MyClass)() + >>>> v += [MyClass(1), MyClass(2), MyClass(3)] + >>>> for m in v: + .... print m.m_i, + .... + 1 2 3 + >>>> + +Other templates work similarly. +The arguments to the template instantiation can either be a string with the +full list of arguments, or the explicit classes. +The latter makes for easier code writing if the classes passed to the +instantiation are themselves templates. + + The fast lane ============= diff --git a/pypy/doc/windows.rst b/pypy/doc/windows.rst --- a/pypy/doc/windows.rst +++ b/pypy/doc/windows.rst @@ -24,7 +24,8 @@ translation. Failing that, they will pick the most recent Visual Studio compiler they can find. In addition, the target architecture (32 bits, 64 bits) is automatically selected. A 32 bit build can only be built -using a 32 bit Python and vice versa. +using a 32 bit Python and vice versa. By default pypy is built using the +Multi-threaded DLL (/MD) runtime environment. **Note:** PyPy is currently not supported for 64 bit Windows, and translation will fail in this case. @@ -102,10 +103,12 @@ Download the source code of expat on sourceforge: http://sourceforge.net/projects/expat/ and extract it in the base -directory. Then open the project file ``expat.dsw`` with Visual +directory. Version 2.1.0 is known to pass tests. Then open the project +file ``expat.dsw`` with Visual Studio; follow the instruction for converting the project files, -switch to the "Release" configuration, and build the solution (the -``expat`` project is actually enough for pypy). +switch to the "Release" configuration, reconfigure the runtime for +Multi-threaded DLL (/MD) and build the solution (the ``expat`` project +is actually enough for pypy). Then, copy the file ``win32\bin\release\libexpat.dll`` somewhere in your PATH. _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit