From e0c29962e6f334452f0c9db2caaf6ed18065de85 Mon Sep 17 00:00:00 2001 From: Marcelo Lira Date: Mon, 17 Aug 2009 19:31:37 -0300 Subject: The End Is the Beginning Is the End --- .gitignore | 10 + CMakeLists.txt | 54 ++ COPYING | 342 +++++++ Doxyfile | 311 ++++++ cmake_uninstall.cmake | 21 + cppgenerator.cpp | 1453 +++++++++++++++++++++++++++++ cppgenerator.h | 80 ++ debian/changelog | 6 + debian/compat | 1 + debian/control | 25 + debian/copyright | 37 + debian/rules | 15 + doc/Makefile | 88 ++ doc/_static/.gitignore | 0 doc/_templates/index.html | 34 + doc/_templates/layout.html | 10 + doc/compiling/cmake-primer.rst | 68 ++ doc/compiling/compiling.rst | 9 + doc/compiling/setup-apiextractor.rst | 56 ++ doc/compiling/setup-generator.rst | 54 ++ doc/conf.py | 198 ++++ doc/contents.rst | 10 + doc/dependency-pyqtb.svg | 600 ++++++++++++ doc/faq.rst | 107 +++ doc/images/.directory | 3 + doc/images/bindinggen-development.png | Bin 0 -> 34333 bytes doc/images/boostgen.png | Bin 0 -> 153473 bytes doc/images/boostqtarch.png | Bin 0 -> 34257 bytes doc/overview.rst | 46 + doc/tutorial/bindinglibfoo.rst | 76 ++ doc/tutorial/buildingthebinding.rst | 133 +++ doc/tutorial/globalheader.rst | 36 + doc/tutorial/images/generatorworkings.png | Bin 0 -> 37257 bytes doc/tutorial/images/generatorworkings.svg | 392 ++++++++ doc/tutorial/introduction.rst | 31 + doc/tutorial/libfoo.rst | 68 ++ doc/tutorial/typesystemcreation.rst | 136 +++ docgenerator.cpp | 1370 +++++++++++++++++++++++++++ docgenerator.h | 213 +++++ headergenerator.cpp | 412 ++++++++ headergenerator.h | 49 + libshiboken/CMakeLists.txt | 37 + libshiboken/FindShiboken.cmake.in | 13 + libshiboken/basewrapper.cpp | 56 ++ libshiboken/basewrapper.h | 92 ++ libshiboken/bindingmanager.cpp | 107 +++ libshiboken/bindingmanager.h | 66 ++ libshiboken/containers.cpp | 41 + libshiboken/containers.h | 47 + libshiboken/conversions.h | 191 ++++ libshiboken/helper.cpp | 67 ++ libshiboken/helper.h | 72 ++ libshiboken/pyenum.cpp | 96 ++ libshiboken/pyenum.h | 68 ++ libshiboken/shiboken.h | 47 + libshiboken/shiboken.pc.in | 13 + main.cpp | 38 + polymorphicdata.cpp | 386 ++++++++ polymorphicdata.h | 87 ++ shibokengenerator.cpp | 829 ++++++++++++++++ shibokengenerator.h | 213 +++++ tests/CMakeLists.txt | 20 + tests/libsample/CMakeLists.txt | 22 + tests/libsample/abstract.cpp | 64 ++ tests/libsample/abstract.h | 37 + tests/libsample/complex.cpp | 27 + tests/libsample/complex.h | 25 + tests/libsample/derived.cpp | 80 ++ tests/libsample/derived.h | 46 + tests/libsample/functions.cpp | 85 ++ tests/libsample/functions.h | 36 + tests/libsample/implicitconv.cpp | 38 + tests/libsample/implicitconv.h | 44 + tests/libsample/kindergarten.cpp | 64 ++ tests/libsample/kindergarten.h | 29 + tests/libsample/listuser.cpp | 47 + tests/libsample/listuser.h | 23 + tests/libsample/main.cpp | 216 +++++ tests/libsample/modifications.cpp | 68 ++ tests/libsample/modifications.h | 73 ++ tests/libsample/pairuser.cpp | 32 + tests/libsample/pairuser.h | 19 + tests/libsample/point.cpp | 111 +++ tests/libsample/point.h | 51 + tests/libsample/reference.cpp | 11 + tests/libsample/reference.h | 24 + tests/libsample/samplenamespace.cpp | 48 + tests/libsample/samplenamespace.h | 36 + tests/libsample/size.cpp | 11 + tests/libsample/size.h | 152 +++ tests/run_test.sh | 13 + tests/samplebinding/CMakeLists.txt | 44 + tests/samplebinding/abstract_test.py | 62 ++ tests/samplebinding/complex_conversions.h | 23 + tests/samplebinding/complex_test.py | 42 + tests/samplebinding/derived_test.py | 113 +++ tests/samplebinding/enum_test.py | 36 + tests/samplebinding/global.h | 14 + tests/samplebinding/implicitconv_test.py | 27 + tests/samplebinding/list_conversions.h | 29 + tests/samplebinding/modifications_test.py | 115 +++ tests/samplebinding/pair_conversions.h | 25 + tests/samplebinding/point_test.py | 42 + tests/samplebinding/reference_test.py | 28 + tests/samplebinding/sample_test.py | 37 + tests/samplebinding/size_test.py | 83 ++ tests/samplebinding/typesystem_sample.xml | 216 +++++ 107 files changed, 11608 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 Doxyfile create mode 100644 cmake_uninstall.cmake create mode 100644 cppgenerator.cpp create mode 100644 cppgenerator.h create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/rules create mode 100644 doc/Makefile create mode 100644 doc/_static/.gitignore create mode 100644 doc/_templates/index.html create mode 100644 doc/_templates/layout.html create mode 100644 doc/compiling/cmake-primer.rst create mode 100644 doc/compiling/compiling.rst create mode 100644 doc/compiling/setup-apiextractor.rst create mode 100644 doc/compiling/setup-generator.rst create mode 100644 doc/conf.py create mode 100644 doc/contents.rst create mode 100644 doc/dependency-pyqtb.svg create mode 100644 doc/faq.rst create mode 100644 doc/images/.directory create mode 100644 doc/images/bindinggen-development.png create mode 100644 doc/images/boostgen.png create mode 100644 doc/images/boostqtarch.png create mode 100644 doc/overview.rst create mode 100644 doc/tutorial/bindinglibfoo.rst create mode 100644 doc/tutorial/buildingthebinding.rst create mode 100644 doc/tutorial/globalheader.rst create mode 100644 doc/tutorial/images/generatorworkings.png create mode 100644 doc/tutorial/images/generatorworkings.svg create mode 100644 doc/tutorial/introduction.rst create mode 100644 doc/tutorial/libfoo.rst create mode 100644 doc/tutorial/typesystemcreation.rst create mode 100644 docgenerator.cpp create mode 100644 docgenerator.h create mode 100644 headergenerator.cpp create mode 100644 headergenerator.h create mode 100644 libshiboken/CMakeLists.txt create mode 100644 libshiboken/FindShiboken.cmake.in create mode 100644 libshiboken/basewrapper.cpp create mode 100644 libshiboken/basewrapper.h create mode 100644 libshiboken/bindingmanager.cpp create mode 100644 libshiboken/bindingmanager.h create mode 100644 libshiboken/containers.cpp create mode 100644 libshiboken/containers.h create mode 100644 libshiboken/conversions.h create mode 100644 libshiboken/helper.cpp create mode 100644 libshiboken/helper.h create mode 100644 libshiboken/pyenum.cpp create mode 100644 libshiboken/pyenum.h create mode 100644 libshiboken/shiboken.h create mode 100644 libshiboken/shiboken.pc.in create mode 100644 main.cpp create mode 100644 polymorphicdata.cpp create mode 100644 polymorphicdata.h create mode 100644 shibokengenerator.cpp create mode 100644 shibokengenerator.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/libsample/CMakeLists.txt create mode 100644 tests/libsample/abstract.cpp create mode 100644 tests/libsample/abstract.h create mode 100644 tests/libsample/complex.cpp create mode 100644 tests/libsample/complex.h create mode 100644 tests/libsample/derived.cpp create mode 100644 tests/libsample/derived.h create mode 100644 tests/libsample/functions.cpp create mode 100644 tests/libsample/functions.h create mode 100644 tests/libsample/implicitconv.cpp create mode 100644 tests/libsample/implicitconv.h create mode 100644 tests/libsample/kindergarten.cpp create mode 100644 tests/libsample/kindergarten.h create mode 100644 tests/libsample/listuser.cpp create mode 100644 tests/libsample/listuser.h create mode 100644 tests/libsample/main.cpp create mode 100644 tests/libsample/modifications.cpp create mode 100644 tests/libsample/modifications.h create mode 100644 tests/libsample/pairuser.cpp create mode 100644 tests/libsample/pairuser.h create mode 100644 tests/libsample/point.cpp create mode 100644 tests/libsample/point.h create mode 100644 tests/libsample/reference.cpp create mode 100644 tests/libsample/reference.h create mode 100644 tests/libsample/samplenamespace.cpp create mode 100644 tests/libsample/samplenamespace.h create mode 100644 tests/libsample/size.cpp create mode 100644 tests/libsample/size.h create mode 100755 tests/run_test.sh create mode 100644 tests/samplebinding/CMakeLists.txt create mode 100755 tests/samplebinding/abstract_test.py create mode 100644 tests/samplebinding/complex_conversions.h create mode 100755 tests/samplebinding/complex_test.py create mode 100755 tests/samplebinding/derived_test.py create mode 100755 tests/samplebinding/enum_test.py create mode 100644 tests/samplebinding/global.h create mode 100755 tests/samplebinding/implicitconv_test.py create mode 100644 tests/samplebinding/list_conversions.h create mode 100755 tests/samplebinding/modifications_test.py create mode 100644 tests/samplebinding/pair_conversions.h create mode 100755 tests/samplebinding/point_test.py create mode 100755 tests/samplebinding/reference_test.py create mode 100755 tests/samplebinding/sample_test.py create mode 100755 tests/samplebinding/size_test.py create mode 100644 tests/samplebinding/typesystem_sample.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..fa13dcc58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +build +.kdev4 +*.log +*.pyc +*.o +*.so +.preprocessed.tmp +.*.swp +*.kdev4 +doc/_build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..62a9fee54 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,54 @@ +project(shiboken) + +cmake_minimum_required(VERSION 2.6) + +find_package(Qt4 4.5.0 REQUIRED) +find_package(ApiExtractor REQUIRED) + +add_definitions(${QT_DEFINITIONS}) + +set(shiboken_VERSION 0.1) +set(CMAKE_BUILD_TYPE Debug) + +set(shiboken_SRC +shibokengenerator.cpp +headergenerator.cpp +cppgenerator.cpp +docgenerator.cpp +polymorphicdata.cpp +main.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${APIEXTRACTOR_INCLUDE_DIR} + ${APIEXTRACTOR_INCLUDE_DIR}/.. + ${QT_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR}) + +add_executable(shiboken ${shiboken_SRC}) + +target_link_libraries(shiboken + ${APIEXTRACTOR_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTXML_LIBRARY}) + +# uninstall target +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) +add_custom_target(uninstall "${CMAKE_COMMAND}" + -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + +enable_testing() + +add_subdirectory(libshiboken) +add_subdirectory(tests) + +# "make dist", in fact "make package_source" +set(CPACK_SOURCE_PACKAGE_FILE_NAME "shiboken-${shiboken_VERSION}") +set(CPACK_SOURCE_GENERATOR TGZ) +set(CPACK_SOURCE_IGNORE_FILES "~$" ".svn" "debian/" "build/" ".swp$" "*.kdev4") +include(CPack) + +install(TARGETS shiboken DESTINATION bin) + diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..4ccd71466 --- /dev/null +++ b/COPYING @@ -0,0 +1,342 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + +------------------------------------------------------------------------- diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 000000000..24289839e --- /dev/null +++ b/Doxyfile @@ -0,0 +1,311 @@ +# Doxyfile 1.5.7.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "CPython Generator Backend" +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = /tmp/src/cpythonbackend/ +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /tmp/src/cpythonbackend +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHG_LOCATION = +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = ../libgenerator/libgenerator.tag=../libgenerator +GENERATE_TAGFILE = cpythonbackend.tag +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = NO +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = NO +INCLUDED_BY_GRAPH = NO +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/cmake_uninstall.cmake b/cmake_uninstall.cmake new file mode 100644 index 000000000..df95fb9d8 --- /dev/null +++ b/cmake_uninstall.cmake @@ -0,0 +1,21 @@ +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +STRING(REGEX REPLACE "\n" ";" files "${files}") +FOREACH(file ${files}) + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + IF(EXISTS "$ENV{DESTDIR}${file}") + EXEC_PROGRAM( + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval + ) + IF(NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + ENDIF(NOT "${rm_retval}" STREQUAL 0) + ELSE(EXISTS "$ENV{DESTDIR}${file}") + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + ENDIF(EXISTS "$ENV{DESTDIR}${file}") +ENDFOREACH(file) diff --git a/cppgenerator.cpp b/cppgenerator.cpp new file mode 100644 index 000000000..a0de62bbe --- /dev/null +++ b/cppgenerator.cpp @@ -0,0 +1,1453 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "cppgenerator.h" +#include + +#include +#include +#include + +static Indentor INDENT; + +QString CppGenerator::cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName) +{ + return QString("%1_cptr(%2)").arg(cpythonBaseName(metaClass->typeEntry())).arg(argName); +} + +QString CppGenerator::fileNameForClass(const AbstractMetaClass *metaClass) const +{ + return metaClass->qualifiedCppName().toLower() + QLatin1String("_wrapper.cpp"); +} + +QList CppGenerator::filterGroupedFunctions(const AbstractMetaClass* metaClass) +{ + AbstractMetaFunctionList lst; + if (metaClass) + lst = queryFunctions(metaClass, true); + else + lst = globalFunctions(); + + QMap results; + foreach (AbstractMetaFunction* func, lst) { + //skip signals + if (func->isSignal() || func->isDestructor() || (func->isModifiedRemoved() && !func->isAbstract())) + continue; + if (!results.contains(func->name())) + results[func->name()] = AbstractMetaFunctionList(); + results[func->name()].append(func); + } + + //TODO: put these lines back to work + + //append global operators + //lst += queryGlobalOperators(metaClass); + + return results.values(); +} + +QList CppGenerator::filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass, + uint query) +{ + QMap >, AbstractMetaFunctionList> results; + foreach (AbstractMetaFunction* func, metaClass->operatorOverloads(query)) { + if (func->isModifiedRemoved()) + continue; + int args; + bool revOp; + if (func->isComparisonOperator()) { + args = -1; + revOp = false; + } else { + args = func->arguments().size(); + revOp = ShibokenGenerator::isReverseOperator(func); + } + QPair > op(func->name(), + QPair(args, revOp)); + if (!results.contains(op)) + results[op] = AbstractMetaFunctionList(); + results[op].append(func); + } + return results.values(); +} + +/*! + Function used to write the class generated binding code on the buffer + \param s the output buffer + \param metaClass the pointer to metaclass information +*/ +void CppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *metaClass) +{ + ReportHandler::debugSparse("Generating wrapper implementation for " + metaClass->fullName()); + + // write license comment + s << licenseComment() << endl; + + if (!metaClass->isNamespace()) { + //workaround to access protected functions + s << "//workaround to access protected functions" << endl; + s << "#define protected public" << endl << endl; + } + + // headers + s << "// default includes" << endl; + s << "#include " << endl; + s << "#include \"" << moduleName().toLower() << "_python.h\"" << endl << endl; + + QString headerfile = fileNameForClass(metaClass); + headerfile.replace("cpp", "h"); + s << "#include \"" << headerfile << '"' << endl; + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) { + if (shouldGenerate(innerClass)) { + QString headerfile = fileNameForClass(metaClass); + headerfile.replace("cpp", "h"); + s << "#include \"" << headerfile << '"' << endl; + } + } + + //Extra includes + s << endl << "// Extra includes" << endl; + QList includes = metaClass->typeEntry()->extraIncludes(); + qSort(includes.begin(), includes.end()); + foreach (Include inc, includes) + s << inc.toString() << endl; + s << endl; + + if (metaClass->typeEntry()->typeFlags() & ComplexTypeEntry::Deprecated) + s << "#Deprecated" << endl; + + if (!canCreateWrapperFor(metaClass)) + return; + + s << "using namespace Shiboken;" << endl << endl; + + if (!metaClass->isNamespace()) { + s << "// Native ---------------------------------------------------------" << endl << endl; + + //inject code native beginner + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::Beginning, TypeSystem::NativeCode); + + foreach (const AbstractMetaFunction *func, filterFunctions(metaClass)) { + if (func->isPrivate() || (func->isModifiedRemoved() && !func->isAbstract())) + continue; + + if (func->isConstructor() && !func->isCopyConstructor()) { + writeConstructorNative(s, func); + } else if (func->isVirtual() || func->isAbstract()) { + writeVirtualMethodNative(s, func); +// } else if (func->hasInjectedCodeOrSignatureModifications() || +// func->isThread() || func->allowThread()) { +// writeNonVirtualModifiedFunctionNative(s, func); + } + } + + writeDestructorNative(s, metaClass); + + //inject code native end + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::NativeCode); + + s << endl << "// Target ---------------------------------------------------------" << endl << endl; + } + + Indentation indentation(INDENT); + + QString methodsDefinitions; + QTextStream md(&methodsDefinitions); + + bool hasComparisonOperator = false; + bool typeAsNumber = false; + + foreach (AbstractMetaFunctionList allOverloads, filterGroupedFunctions(metaClass)) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + if (!func->isModifiedRemoved() && !func->isPrivate() && + func->ownerClass() == func->implementingClass()) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + const AbstractMetaFunction* rfunc = overloads[0]; + if (rfunc->isConstructor()) + writeConstructorWrapper(s, overloads); + else if (rfunc->isArithmeticOperator() + || rfunc->isLogicalOperator() + || rfunc->isBitwiseOperator()) + typeAsNumber = true; + else if (rfunc->isComparisonOperator()) + hasComparisonOperator = true; + else + writeMethodWrapper(s, overloads); + + if (!rfunc->isConstructor() && !rfunc->isOperatorOverload()) + writeMethodDefinition(md, overloads); + } + + // Write methods definition + s << "static PyMethodDef Py" << metaClass->qualifiedCppName() << "_methods[] = {" << endl; + s << methodsDefinitions << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl; + + if (typeAsNumber) { + QList opOverloads = filterGroupedOperatorFunctions( + metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + foreach (AbstractMetaFunctionList allOverloads, opOverloads) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, allOverloads) { + if (!func->isModifiedRemoved() + && !func->isPrivate() + && func->ownerClass() == func->implementingClass()) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + writeMethodWrapper(s, overloads); + } + + s << "// type has number operators" << endl; + writeTypeAsNumberDefinition(s, metaClass); + } + + if (hasComparisonOperator) { + s << "// Rich comparison" << endl; + writeRichCompareFunction(s, metaClass); + } + + s << "extern \"C\"" << endl << '{' << endl << endl; + writeClassDefinition(s, metaClass); + s << endl; + + foreach (AbstractMetaEnum* cppEnum, metaClass->enums()) + writeEnumDefinition(s, cppEnum); + s << endl; + + writeClassRegister(s, metaClass); + s << endl << "} // extern \"C\"" << endl; +} + +void CppGenerator::writeConstructorNative(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + s << functionSignature(func, wrapperName(func->ownerClass()) + "::", "", + (Option)(OriginalTypeDescription | SkipDefaultValues)); + s << " : "; + writeFunctionCall(s, func); + s << " {" << endl; + writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func); + s << INDENT << "// ... middle" << endl; + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); + s << '}' << endl << endl; +} + +void CppGenerator::writeDestructorNative(QTextStream &s, const AbstractMetaClass *metaClass) +{ + s << wrapperName(metaClass) << "::~" << wrapperName(metaClass) << "()" << endl << '{' << endl; + s << '}' << endl; +} + +void CppGenerator::writeVirtualMethodNative(QTextStream &s, const AbstractMetaFunction* func) +{ + QString returnKeyword = func->type() ? QLatin1String("return ") : QString(); + QString prefix = wrapperName(func->ownerClass()) + "::"; + s << functionSignature(func, prefix, "", Generator::SkipDefaultValues) << endl << "{" << endl; + + Indentation indentation(INDENT); + + if (func->hasInjectedCode()) { + writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::NativeCode, func); + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::NativeCode, func); + } else if (func->isAbstract() && func->isModifiedRemoved()) { + s << INDENT << "#error Pure virtual method \"" << func->ownerClass()->name(); + s << "::" << func->minimalSignature(); + s << "\" must be implement but was completely removed on typesystem." << endl; + } else { +// QString converter_calls; +// QTextStream s_converter_calls(&converter_calls); +// QString callDefault = returnKeyword + "default_method(self"; +// foreach (const AbstractMetaArgument* arg, func->arguments()) { +// Indentation indentation(INDENT); +// callDefault += ", " + arg->argumentName(); +// s_converter_calls << INDENT << ", " << '&' << arg->argumentName() +// if ((arg->type()->isQObject() || arg->type()->isObject() || arg->type()->isValue()) && +// !arg->type()->isReference()) { +// s_converter_calls << '&' << arg->argumentName() << ", nagasaki::to_python_converter" << endl; +// } else { +// s_converter_calls << "nagasaki::convert_to_python< " +// << argumentString(func, arg, SkipName | SkipDefaultValues) +// << " >(" << arg->argumentName() << ')' << endl; +// } +// } +// callDefault += ");"; + if (func->allowThread()) + s << INDENT << "// how to say to Python to allow threads?" << endl; + + s << INDENT << "PyObject* method = BindingManager::instance().getOverride(this, \""; + s << func->name() << "\");" << endl; + + s << INDENT << "if (!method) {" << endl; + { + Indentation indentation(INDENT); + s << INDENT; + if (func->isAbstract()) { + s << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; + s << func->ownerClass()->name() << '.' << func->name(); + s << "()' not implemented.\");" << endl; + s << INDENT << "return"; + } else { + s << "return this->" << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func); + } + } + s << ';' << endl << INDENT << '}' << endl << endl; + + s << INDENT << "PyObject* args = "; + if (func->arguments().isEmpty()) { + s << "PyTuple_New(0);" << endl; + } else { + s << "Py_BuildValue(\"(" << getFormatUnitString(func->arguments()) << ")\""; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + s << ", " << arg->argumentName(); + if ((arg->type()->isObject() || arg->type()->isValue()) && !arg->type()->isReference()) { + s << ", Shiboken::Converter< " + << translateType(arg->type(), func->ownerClass()) + << " >::toPython"; + } + } + s << ");" << endl; + } + s << endl; + + s << INDENT << "PyGILState_STATE gil_state = PyGILState_Ensure();" << endl; + + s << INDENT; + if (!returnKeyword.isEmpty()) + s << "PyObject* method_result = "; + s << "PyObject_Call(method, args, NULL);" << endl; + s << INDENT << "PyGILState_Release(gil_state);" << endl << endl; + s << INDENT << "Py_XDECREF(args);" << endl; + s << INDENT << "Py_XDECREF(method);" << endl; + + s << endl << INDENT << "// check and set Python error here..." << endl; + } + + if (!returnKeyword.isEmpty()) { + s << INDENT << returnKeyword; + writeToCppConversion(s, func->type(), func->implementingClass(), "method_result"); + s << ';' << endl; + } + s << '}' << endl << endl; +} + +void CppGenerator::writeNonVirtualModifiedFunctionNative(QTextStream& s, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + + s << getFunctionReturnType(func) << ' '; + s << func->ownerClass()->name() << '_' << func->originalName() << "_modified("; + + uint options = SkipRemovedArguments | SkipDefaultValues; + if (!func->isStatic()) + options |= WriteSelf; + + writeFunctionArguments(s, func, options); + s << ")" << endl << "{" << endl; + + if (func->isThread()) + s << INDENT << "thread_locker lock;" << endl; + + if (func->allowThread()) + s << INDENT << "py_allow_threads allow_threads;" << endl; + + if (!getCodeSnips(func).isEmpty()) { + writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func); + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); + } else { + s << INDENT; + if (func->type()) + s << "return "; + if (func->isStatic()) + s << func->declaringClass()->name() << "::"; + else + s << "self."; + writeFunctionCall(s, func); + s << ";" << endl; + } + + s << '}' << endl << endl; +} + +void CppGenerator::writeConstructorWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + PolymorphicData polymorphicData(overloads); + const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction(); + QString className = rfunc->ownerClass()->qualifiedCppName(); + + s << "PyObject*" << endl; + s << cpythonFunctionName(rfunc) << "(PyTypeObject *type, PyObject *args, PyObject *kwds)" << endl; + s << '{' << endl; + + s << INDENT << "PyObject* self;" << endl; + s << INDENT << getFunctionReturnType(rfunc) << " cptr;" << endl << endl; + + if (rfunc->ownerClass()->isAbstract()) { + s << INDENT << "if (type == &Py" << className << "_Type) {" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError," << endl; + s << INDENT << INDENT << "\"'" << className; + s << "' represents a C++ abstract class and cannot be instanciated\");" << endl; + s << INDENT << "return 0;" << endl; + } + s << INDENT << '}' << endl << endl; + } + + s << INDENT << "if (!PyType_IsSubtype(type, &Py" << className << "_Type))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + + if (polymorphicData.maxArgs() > 0) { + s << endl << INDENT << "int numArgs = "; + writeArgumentsInitializer(s, polymorphicData); + } + + writeCodeSnips(s, getCodeSnips(rfunc), CodeSnip::Beginning, TypeSystem::All, rfunc); + + writePolymorphicDecisor(s, &polymorphicData); + s << endl; + + s << INDENT << "self = Shiboken::PyBaseWrapper_New(type, &Py" << className << "_Type, cptr);" << endl; + s << endl << INDENT << "if (!self) {" << endl; + { + Indentation indentation(INDENT); + s << INDENT << "if (cptr) delete cptr;" << endl; + s << INDENT << "return 0;" << endl; + } + s << INDENT << '}' << endl; + + writeCodeSnips(s, getCodeSnips(rfunc), CodeSnip::End, TypeSystem::All, rfunc); + + s << endl << INDENT << "return self;" << endl; + if (polymorphicData.maxArgs() > 0) + writeErrorSection(s, polymorphicData); + s << '}' << endl << endl; +} + +void CppGenerator::writeMethodWrapper(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + PolymorphicData polymorphicData(overloads); + const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction(); + + //DEBUG + //if (rfunc->isOperatorOverload()) { + // QString dumpFile = QString("%1_%2.dot").arg(m_packageName).arg(pythonOperatorFunctionName(rfunc)).toLower(); + // polymorphicData.dumpGraph(dumpFile); + //} + //DEBUG + + // TODO: take this off when operator generation is fixed + // if (rfunc->isOperatorOverload()) +// if (rfunc->isInplaceOperator()) +// s << "/*" << endl; + + int minArgs = polymorphicData.minArgs(); + int maxArgs = polymorphicData.maxArgs(); + if (ShibokenGenerator::isReverseOperator(rfunc)) { + minArgs--; + maxArgs--; + } + + s << "static PyObject*" << endl; + s << cpythonFunctionName(rfunc) << "(PyObject* self"; + if (maxArgs > 0) { + s << ", PyObject* arg"; + if (maxArgs > 1) + s << 's'; + } + s << ')' << endl << '{' << endl; + + if (rfunc->isAbstract()) { + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"pure virtual method '"; + s << rfunc->ownerClass()->name() << '.' << rfunc->name(); + s << "()' not implemented.\");" << endl; + s << INDENT << "return 0;" << endl; + } else { + if (rfunc->implementingClass() && + (!rfunc->implementingClass()->isNamespace() && !rfunc->isStatic())) { + // Checks if the underlying C++ object is valid. + // If the wrapped C++ library have no function that steals ownership and + // deletes the C++ object this check would not be needed. + s << INDENT << "if (!Shiboken::cppObjectIsValid((Shiboken::PyBaseWrapper*)self))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + } + + if (rfunc->type()) + s << INDENT << getFunctionReturnType(rfunc) << ' ' << retvalVariableName() << ';' << endl; + + if (minArgs != maxArgs || maxArgs > 1) { + s << INDENT << "int numArgs = "; + if (minArgs == 0 && maxArgs == 1) + s << "(arg == 0 ? 0 : 1);" << endl; + else + writeArgumentsInitializer(s, polymorphicData); + } + + writePolymorphicDecisor(s, &polymorphicData); + + s << endl << INDENT << "if (PyErr_Occurred())" << endl; + s << INDENT << INDENT << "return 0;" << endl; + + s << INDENT; + if (rfunc->type()) { + s << "return "; + if (rfunc->isInplaceOperator()) + s << "self"; + else + writeToPythonConversion(s, rfunc->type(), rfunc->ownerClass(), retvalVariableName()); + } else { + s << "Py_RETURN_NONE"; + } + s << ';' << endl; + + if (maxArgs > 0) + writeErrorSection(s, polymorphicData); + } + s << '}' << endl << endl; + + // TODO: take this off when operator generation is fixed +// if (rfunc->isOperatorOverload()) +// if (rfunc->isInplaceOperator()) +// s << "*/" << endl; +} + +void CppGenerator::writeArgumentsInitializer(QTextStream& s, PolymorphicData& polymorphicData) +{ + const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction(); + s << "PyTuple_GET_SIZE(args);" << endl; + + s << INDENT << "PyObject* pyargs[] = {"; + s << QString(polymorphicData.maxArgs(), '0').split("", QString::SkipEmptyParts).join(", "); + s << "};" << endl << endl; + + QStringList palist; + for (int i = 0; i < polymorphicData.maxArgs(); i++) + palist << QString("&(pyargs[%1])").arg(i); + QString pyargs = palist.join(", "); + + QList invalidArgsLength = polymorphicData.invalidArgumentLengths(); + if (!invalidArgsLength.isEmpty()) { + QStringList invArgsLen; + foreach (int i, invalidArgsLength) + invArgsLen << QString("numArgs == %1").arg(i); + s << INDENT << "// invalid argument lengths" << endl; + s << INDENT << "if (" << invArgsLen.join(" || ") << ")" << endl; + s << INDENT << INDENT << "goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl << endl; + } + + QString funcName; + if (rfunc->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + else + funcName = rfunc->name(); + + s << INDENT << "if (!PyArg_UnpackTuple(args, \"" << funcName << "\", "; + s << polymorphicData.minArgs() << ", " << polymorphicData.maxArgs(); + s << ", " << pyargs << "))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return 0;" << endl; + } + s << endl; +} + +void CppGenerator::writeErrorSection(QTextStream& s, PolymorphicData& polymorphicData) +{ + const AbstractMetaFunction* rfunc = polymorphicData.referenceFunction(); + s << endl << INDENT << cpythonFunctionName(rfunc) << "_TypeError:" << endl; + Indentation indentation(INDENT); + QString funcName; + if (rfunc->isOperatorOverload()) + funcName = ShibokenGenerator::pythonOperatorFunctionName(rfunc); + else + funcName = rfunc->name(); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'" << funcName; + s << "()' called with wrong parameters.\");" << endl; + s << INDENT << "return 0;" << endl; +} + +void CppGenerator::writeTypeCheck(QTextStream& s, const PolymorphicData* polyData, QString argumentName) +{ + const AbstractMetaType* argType = polyData->argType(); + AbstractMetaFunctionList implicitConverters; + if (argType->isValue()) { + const AbstractMetaClass* metaClass = classes().findClass(argType->name()); + if (metaClass) + implicitConverters = metaClass->implicitConversions(); + } + + int alternativeNumericTypes = 0; + foreach (PolymorphicData* pd, polyData->polymorphicDataOnPosition(polyData->argPos())) { + if (!pd->argType()->isPrimitive()) + continue; + if (ShibokenGenerator::isNumber(pd->argType()->typeEntry())) + alternativeNumericTypes++; + } + + // This condition trusts that the PolymorphicData object will arrange for + // PyInt type to be the last entry on a list of polymorphic argument data. + bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(argType); + + if (implicitConverters.size() > 0) + s << '('; + + s << cpythonCheckFunction(argType, numberType) << '(' << argumentName << ')'; + + foreach (const AbstractMetaFunction* ctor, implicitConverters) { + s << " || "; + s << cpythonCheckFunction(ctor->arguments().first()->type(), numberType); + s << '(' << argumentName << ')'; + } + + if (implicitConverters.size() > 0) + s << ')'; +} + +void CppGenerator::writePolymorphicDecisor(QTextStream& s, PolymorphicData* parentPolymorphicData) +{ + bool hasDefaultCall = parentPolymorphicData->nextArgumentHasDefaultValue(); + if (!hasDefaultCall && parentPolymorphicData->isHeadPolymorphicData()) { + foreach (const AbstractMetaFunction* func, parentPolymorphicData->overloads()) { + if (parentPolymorphicData->isFinalOccurrence(func)) { + hasDefaultCall = true; + break; + } + } + } + + const AbstractMetaFunction* rfunc = parentPolymorphicData->referenceFunction(); + + int minArgs = parentPolymorphicData->minArgs(); + int maxArgs = parentPolymorphicData->maxArgs(); + if (ShibokenGenerator::isReverseOperator(rfunc)) { + minArgs--; + maxArgs--; + } + + if (maxArgs == 0 + || (!parentPolymorphicData->isHeadPolymorphicData() + && (parentPolymorphicData->nextPolymorphicData().isEmpty() + || (!hasDefaultCall && parentPolymorphicData->overloads().size() == 1)))) { + const AbstractMetaFunction* func = parentPolymorphicData->overloads()[0]; + int removed = PolymorphicData::numberOfRemovedArguments(func); + writeMethodCall(s, func, func->arguments().size() - removed); + return; + } + + bool varargs = maxArgs > 1 || rfunc->isConstructor(); + + s << INDENT; + + // can make a default call + if (hasDefaultCall) { + s << "if (numArgs == " << parentPolymorphicData->argPos() + 1 << ") { // hasDefaultCall" << endl; + { + Indentation indent(INDENT); + writeMethodCall(s, rfunc, parentPolymorphicData->argPos() + 1); + } + s << INDENT << "} else "; + } + + // last occurrence of function signature + if (!parentPolymorphicData->isHeadPolymorphicData()) { + foreach (const AbstractMetaFunction* func, parentPolymorphicData->overloads()) { + if (parentPolymorphicData->isFinalOccurrence(func)) { + int lastArg = parentPolymorphicData->argPos() + 1; + s << "if (numArgs == " << lastArg << ") { // final:" << func->minimalSignature() << endl; + { + Indentation indent(INDENT); + writeMethodCall(s, func, lastArg); + } + s << INDENT << "} else "; + } + } + } + + foreach (PolymorphicData* polymorphicData, parentPolymorphicData->nextPolymorphicData()) { + if (maxArgs > 0) { + bool signatureFound = polymorphicData->overloads().size() == 1 && + !polymorphicData->nextArgumentHasDefaultValue(); + const AbstractMetaFunction* func = polymorphicData->overloads()[0]; + QString pyArgName = varargs ? QString("pyargs[%1]").arg(polymorphicData->argPos()) : "arg"; + + s << "if ("; + if (signatureFound && varargs) { + s << "numArgs == "; + s << func->arguments().size() - PolymorphicData::numberOfRemovedArguments(func); + s << " && "; + } + + writeTypeCheck(s, polymorphicData, pyArgName); + + if (polymorphicData->argType()->isContainer() && + ((ContainerTypeEntry*)polymorphicData->argType()->typeEntry())->type() + == ContainerTypeEntry::PairContainer) { + s << " && PyTuple_GET_SIZE(" << pyArgName << ") == 2"; + } + + if (signatureFound && varargs) { + int numArgs = func->arguments().size() - PolymorphicData::numberOfRemovedArguments(func); + PolymorphicData* tmp = polymorphicData; + for (int i = polymorphicData->argPos() + 1; i < numArgs; i++) { + tmp = tmp->nextPolymorphicData()[0]; + s << " && "; + writeTypeCheck(s, tmp, QString("pyargs[%1]").arg(i)); + } + } + s << ") {" << endl; + { + Indentation indent(INDENT); + int allRemoved = PolymorphicData::numberOfRemovedArguments(func); + int lastArg = signatureFound ? func->arguments().size() - allRemoved + : polymorphicData->argPos() + 1; + int removed = 0; + for (int i = polymorphicData->argPos(); i < lastArg; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + QString argName = QString("cpp_arg%1").arg(i); + if (varargs) + pyArgName = QString("pyargs[%1]").arg(i); + const AbstractMetaType* type = func->arguments()[i + removed]->type(); + s << INDENT << translateType(type, func->implementingClass()) << ' ' << argName << " = "; + writeToCppConversion(s, type, func->implementingClass(), pyArgName); + s << ';' << endl; + } + } + } + + { + Indentation indent(INDENT); + writePolymorphicDecisor(s, polymorphicData); + } + + s << INDENT << "} else "; + } + if (maxArgs > 0) + s << "goto " << cpythonFunctionName(rfunc) << "_TypeError;" << endl; +} + +void CppGenerator::writeMethodCall(QTextStream& s, const AbstractMetaFunction* func, int lastArg) +{ + s << INDENT << "// " << func->minimalSignature() << endl; + + bool writeCall = true; + if (func->hasSignatureModifications()) { + CodeSnipList snips = getCodeSnips(func); + foreach (CodeSnip snip, snips) { + if (snip.position == CodeSnip::Beginning) { + // modified signature was provided with custom code for the method call + writeCall = false; + break; + } + } + writeCodeSnips(s, snips, CodeSnip::Beginning, TypeSystem::All, func); + } + + if (writeCall) { + bool badModifications = false; + QStringList userArgs; + if (!func->isCopyConstructor()) { + int removed = 0; + for (int i = 0; i < lastArg + removed; i++) { + const AbstractMetaArgument* arg = func->arguments()[i]; + if (func->argumentRemoved(i + 1)) { + // If some argument with default value is removed from a + // method signature, the said value must be explicitly + // added to the method call. + removed++; + if (arg->defaultValueExpression().isEmpty()) + badModifications = true; + else + userArgs << arg->defaultValueExpression(); + } else { + userArgs << QString("cpp_arg%1").arg(arg->argumentIndex() - removed); + } + } + + // If any argument's default value was modified the method must be called + // with this new value whenever the user doesn't pass a explicit value to it. + // Also, any unmodified default value coming after the last user specified + // argument and before the modified argument must be splicitly stated. + QStringList otherArgs; + bool defaultModified = false; + for (int i = func->arguments().size() - 1; i >= lastArg; i--) { + const AbstractMetaArgument* arg = func->arguments()[i]; + defaultModified = defaultModified || arg->defaultValueExpression() != arg->originalDefaultValueExpression(); + if (defaultModified) { + if (arg->defaultValueExpression().isEmpty()) + badModifications = true; + else + otherArgs.prepend(arg->defaultValueExpression()); + } + } + + userArgs += otherArgs; + } + + if (badModifications) { + // When an argument is removed from a method signature and no other + // means of calling the method is provided the generator must write + // a compiler error line stating the situation. + s << INDENT << "#error No way to call \"" << func->ownerClass()->name(); + s << "::" << func->minimalSignature(); + s << "\" with the modifications provided on typesystem file" << endl; + } else if (func->isOperatorOverload()) { + QString firstArg("cpp_arg0"); + QString secondArg("cpp_arg0"); + QString selfArg = QString("(*%1)").arg(cpythonWrapperCPtr(func->ownerClass())); + + if (ShibokenGenerator::isReverseOperator(func) || func->isUnaryOperator()) + secondArg = selfArg; + else + firstArg = selfArg; + + QString op = func->originalName(); + op = op.right(op.size() - QString("operator").size()); + + s << INDENT; + if (!func->isInplaceOperator()) + s << retvalVariableName() << " = "; + if (func->isBinaryOperator()) + s << firstArg << ' '; + s << op << ' ' << secondArg << ';';// << endl; + s << "// " << func->minimalSignature() << func->isOperatorOverload() << func->isReverseOperator() << func->isUnaryOperator() << endl; + } else { + s << INDENT; + if (func->isConstructor() || func->isCopyConstructor()) { + s << "cptr = new " << wrapperName(func->ownerClass()); + } else { + if (func->type()) + s << retvalVariableName() << " = "; + if (func->ownerClass()) { + if (!func->isStatic()) + s << cpythonWrapperCPtr(func->ownerClass()) << "->"; + s << func->ownerClass()->name() << "::"; + } + s << func->originalName(); + } + + s << '('; + if (func->isCopyConstructor()) + s << "*((" << func->ownerClass()->qualifiedCppName() << "*)cpp_arg0)"; + else + s << userArgs.join(", "); + s << ");" << endl; + } + } + + writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); +} + +void CppGenerator::writeClassDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString tp_flags; + QString tp_new; + QString tp_dealloc; + QString tp_as_number = QString('0'); + QString tp_richcompare = QString('0'); + QString className = metaClass->qualifiedCppName(); + QString baseClassName; + + if (metaClass->hasArithmeticOperatorOverload() + || metaClass->hasLogicalOperatorOverload() + || metaClass->hasBitwiseOperatorOverload()) { + tp_as_number = QString("&Py%1_as_number").arg(metaClass->qualifiedCppName()); + } + + if (metaClass->baseClass()) + baseClassName = QString("&") + cpythonTypeName(metaClass->baseClass()->typeEntry()); + else + baseClassName = QString("0"); + + if (metaClass->isNamespace()) { + tp_flags = QString("Py_TPFLAGS_HAVE_CLASS"); + tp_new = QString("0"); + tp_dealloc = QString("0"); + } else { + tp_flags = "Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE"; + tp_new = QString("(newfunc)Py%1_New").arg(className); + tp_dealloc = QString("(destructor)&(Shiboken::PyBaseWrapper_Dealloc< %1 >)").arg(className); + if (metaClass->hasComparisonOperatorOverload()) + tp_richcompare = QString("(richcmpfunc)Py%1_richcompare").arg(className); + } + + s << "// Class Definition -----------------------------------------------" << endl; + + s << "PyTypeObject Py" << className << "_Type = {" << endl; + s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl; + s << INDENT << "/*ob_size*/ 0," << endl; + s << INDENT << "/*tp_name*/ const_cast(\"" << className << "\")," << endl; + s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::PyBaseWrapper)," << endl; + s << INDENT << "/*tp_itemsize*/ 0," << endl; + s << INDENT << "/*tp_dealloc*/ " << tp_dealloc << ',' << endl; + s << INDENT << "/*tp_print*/ 0," << endl; + s << INDENT << "/*tp_getattr*/ 0," << endl; + s << INDENT << "/*tp_setattr*/ 0," << endl; + s << INDENT << "/*tp_compare*/ 0," << endl; + s << INDENT << "/*tp_repr*/ 0," << endl; + s << INDENT << "/*tp_as_number*/ " << tp_as_number << ',' << endl; + s << INDENT << "/*tp_as_sequence*/ 0," << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ 0," << endl; + s << INDENT << "/*tp_call*/ 0," << endl; + s << INDENT << "/*tp_str*/ 0," << endl; + s << INDENT << "/*tp_getattro*/ 0," << endl; + s << INDENT << "/*tp_setattro*/ 0," << endl; + s << INDENT << "/*tp_as_buffer*/ 0," << endl; + s << INDENT << "/*tp_flags*/ " << tp_flags << ',' << endl; + s << INDENT << "/*tp_doc*/ 0," << endl; + s << INDENT << "/*tp_traverse*/ 0," << endl; + s << INDENT << "/*tp_clear*/ 0," << endl; + s << INDENT << "/*tp_richcompare*/ " << tp_richcompare << ',' << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ 0," << endl; + s << INDENT << "/*tp_iternext*/ 0," << endl; + s << INDENT << "/*tp_methods*/ Py" << className << "_methods," << endl; + s << INDENT << "/*tp_members*/ 0," << endl; + s << INDENT << "/*tp_getset*/ 0," << endl; + s << INDENT << "/*tp_base*/ " << baseClassName << ',' << endl; + s << INDENT << "/*tp_dict*/ 0," << endl; + s << INDENT << "/*tp_descr_get*/ 0," << endl; + s << INDENT << "/*tp_descr_set*/ 0," << endl; + s << INDENT << "/*tp_dictoffset*/ 0," << endl; + s << INDENT << "/*tp_init*/ 0," << endl; + s << INDENT << "/*tp_alloc*/ PyType_GenericAlloc," << endl; + s << INDENT << "/*tp_new*/ " << tp_new << ',' << endl; + s << INDENT << "/*tp_free*/ PyObject_Del," << endl; + s << INDENT << "/*tp_is_gc*/ 0," << endl; + s << INDENT << "/*tp_bases*/ 0," << endl; + s << INDENT << "/*tp_mro*/ 0," << endl; + s << INDENT << "/*tp_cache*/ 0," << endl; + s << INDENT << "/*tp_subclasses*/ 0," << endl; + s << INDENT << "/*tp_weaklist*/ 0" << endl; + s << "};" << endl << endl; +} + +void CppGenerator::writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QMap nb; + + nb["__add__"] = QString('0'); + nb["__sub__"] = QString('0'); + nb["__mul__"] = QString('0'); + nb["__div__"] = QString('0'); + nb["__mod__"] = QString('0'); + nb["__neg__"] = QString('0'); + nb["__pos__"] = QString('0'); + nb["__invert__"] = QString('0'); + nb["__lshift__"] = QString('0'); + nb["__rshift__"] = QString('0'); + nb["__and__"] = QString('0'); + nb["__xor__"] = QString('0'); + nb["__or__"] = QString('0'); + nb["__iadd__"] = QString('0'); + nb["__isub__"] = QString('0'); + nb["__imul__"] = QString('0'); + nb["__idiv__"] = QString('0'); + nb["__imod__"] = QString('0'); + nb["__ilshift__"] = QString('0'); + nb["__irshift__"] = QString('0'); + nb["__iand__"] = QString('0'); + nb["__ixor__"] = QString('0'); + nb["__ior__"] = QString('0'); + + QList opOverloads = + filterGroupedOperatorFunctions(metaClass, + AbstractMetaClass::ArithmeticOp + | AbstractMetaClass::LogicalOp + | AbstractMetaClass::BitwiseOp); + + foreach (AbstractMetaFunctionList opOverload, opOverloads) { + const AbstractMetaFunction* rfunc = opOverload[0]; + QString opName = ShibokenGenerator::pythonOperatorFunctionName(rfunc->originalName()); + nb[opName] = cpythonFunctionName(rfunc); + } + + s << "static PyNumberMethods Py" << metaClass->qualifiedCppName(); + s << "_as_number = {" << endl; + s << INDENT << "/*nb_add*/ (binaryfunc)" << nb["__add__"] << ',' << endl; + s << INDENT << "/*nb_subtract*/ (binaryfunc)" << nb["__sub__"] << ',' << endl; + s << INDENT << "/*nb_multiply*/ (binaryfunc)" << nb["__mul__"] << ',' << endl; + s << INDENT << "/*nb_divide*/ (binaryfunc)" << nb["__div__"] << ',' << endl; + s << INDENT << "/*nb_remainder*/ (binaryfunc)" << nb["__mod__"] << ',' << endl; + s << INDENT << "/*nb_divmod*/ 0," << endl; + s << INDENT << "/*nb_power*/ 0," << endl; + s << INDENT << "/*nb_negative*/ (unaryfunc)" << nb["__neg__"] << ',' << endl; + s << INDENT << "/*nb_positive*/ (unaryfunc)" << nb["__pos__"] << ',' << endl; + s << INDENT << "/*nb_absolute*/ 0," << endl; + s << INDENT << "/*nb_nonzero*/ 0," << endl; + s << INDENT << "/*nb_invert*/ (unaryfunc)" << nb["__invert__"] << ',' << endl; + s << INDENT << "/*nb_lshift*/ (binaryfunc)" << nb["__lshift__"] << ',' << endl; + s << INDENT << "/*nb_rshift*/ (binaryfunc)" << nb["__rshift__"] << ',' << endl; + s << INDENT << "/*nb_and*/ (binaryfunc)" << nb["__and__"] << ',' << endl; + s << INDENT << "/*nb_xor*/ (binaryfunc)" << nb["__xor__"] << ',' << endl; + s << INDENT << "/*nb_or*/ (binaryfunc)" << nb["__or__"] << ',' << endl; + s << INDENT << "/*nb_coerce*/ 0," << endl; + s << INDENT << "/*nb_int*/ 0," << endl; + s << INDENT << "/*nb_long*/ 0," << endl; + s << INDENT << "/*nb_float*/ 0," << endl; + s << INDENT << "/*nb_oct*/ 0," << endl; + s << INDENT << "/*nb_hex*/ 0," << endl; + s << INDENT << "/*nb_inplace_add*/ (binaryfunc)" << nb["__iadd__"] << ',' << endl; + s << INDENT << "/*nb_inplace_subtract*/ (binaryfunc)" << nb["__isub__"] << ',' << endl; + s << INDENT << "/*nb_inplace_multiply*/ (binaryfunc)" << nb["__imul__"] << ',' << endl; + s << INDENT << "/*nb_inplace_divide*/ (binaryfunc)" << nb["__idiv__"] << ',' << endl; + s << INDENT << "/*nb_inplace_remainder*/ (binaryfunc)" << nb["__imod__"] << ',' << endl; + s << INDENT << "/*nb_inplace_power*/ 0," << endl; + s << INDENT << "/*nb_inplace_lshift*/ (binaryfunc)" << nb["__ilshift__"] << ',' << endl; + s << INDENT << "/*nb_inplace_rshift*/ (binaryfunc)" << nb["__irshift__"] << ',' << endl; + s << INDENT << "/*nb_inplace_and*/ (binaryfunc)" << nb["__iand__"] << ',' << endl; + s << INDENT << "/*nb_inplace_xor*/ (binaryfunc)" << nb["__ixor__"] << ',' << endl; + s << INDENT << "/*nb_inplace_or*/ (binaryfunc)" << nb["__ior__"] << ',' << endl; + s << INDENT << "/*nb_floor_divide*/ 0," << endl; + s << INDENT << "/*nb_true_divide*/ 0," << endl; + s << INDENT << "/*nb_inplace_floor_divide*/ 0," << endl; + s << INDENT << "/*nb_inplace_true_divide*/ 0," << endl; + s << INDENT << "/*nb_index*/ 0" << endl; + s << "};" << endl << endl; +} + +void CppGenerator::writeRichCompareFunction(QTextStream& s, const AbstractMetaClass* metaClass) +{ + s << "static PyObject*" << endl; + s << "Py" << metaClass->qualifiedCppName(); + s << "_richcompare(PyObject* self, PyObject* other, int op)" << endl; + s << '{' << endl; + + QList cmpOverloads = filterGroupedOperatorFunctions(metaClass, AbstractMetaClass::ComparisonOp); + + s << INDENT << "bool result;" << endl; + + QString arg0TypeName = metaClass->qualifiedCppName(); + s << INDENT << arg0TypeName << "& cpp_self = *" << cpythonWrapperCPtr(metaClass) << ';' << endl; + s << endl; + + s << INDENT << "switch (op) {" << endl; + { + Indentation indent(INDENT); + foreach (AbstractMetaFunctionList overloads, cmpOverloads) { + PolymorphicData polyData(overloads); + const AbstractMetaFunction* rfunc = overloads[0]; + + // DEBUG + // QString dumpFile = QString("%1_%2.dot").arg(rfunc->ownerClass()->name()).arg(pythonOperatorFunctionName(rfunc)).toLower(); + // polyData.dumpGraph(dumpFile); + // DEBUG + + s << INDENT << "case " << ShibokenGenerator::pythonRichCompareOperatorId(rfunc) << ':' << endl; + + Indentation indent(INDENT); + + QString op = rfunc->originalName(); + op = op.right(op.size() - QString("operator").size()); + + int alternativeNumericTypes = 0; + foreach (const AbstractMetaFunction* func, overloads) { + if (!func->isStatic() && + ShibokenGenerator::isNumber(func->arguments()[0]->type()->typeEntry())) + alternativeNumericTypes++; + } + + bool first = true; + foreach (const AbstractMetaFunction* func, overloads) { + if (func->isStatic()) + continue; + + const AbstractMetaType* type = func->arguments()[0]->type(); + bool numberType = alternativeNumericTypes == 1 || ShibokenGenerator::isPyInt(type); + + if (!first) { + s << " else "; + } else { + first = false; + s << INDENT; + } + + s << "if (" << cpythonCheckFunction(type, numberType) << "(other)) {" << endl; + { + Indentation indent(INDENT); + s << INDENT; + if (type->isValue() || type->isObject()) { + s << arg0TypeName << "& cpp_other = *"; + s << cpythonWrapperCPtr(metaClass, "other"); + } else { + s << translateType(type, metaClass) << " cpp_other = "; + writeToCppConversion(s, type, metaClass, "other"); + } + s << ';' << endl; + s << INDENT << "result = (cpp_self " << op << " cpp_other);" << endl; + } + s << INDENT << '}'; + } + s << " else goto Py" << metaClass->name() << "_RichComparison_TypeError;" << endl; + s << endl; + + s << INDENT << "break;" << endl; + } + s << INDENT << "default:" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_NotImplementedError, \"operator not implemented.\");" << endl; + s << INDENT << "return 0;" << endl; + } + } + s << INDENT << '}' << endl << endl; + + s << INDENT << "if (result)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "Py_RETURN_TRUE;" << endl; + } + s << INDENT << "Py_RETURN_FALSE;" << endl << endl; + + s << INDENT << "Py" << metaClass->name() << "_RichComparison_TypeError:" << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyErr_SetString(PyExc_TypeError, \"operator called with wrong parameters.\");" << endl; + s << INDENT << "return 0;" << endl; + } + s << '}' << endl << endl; +} + +void CppGenerator::writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads) +{ + QPair minMax = PolymorphicData::getMinMaxArguments(overloads); + const AbstractMetaFunction* func = overloads[0]; + + s << INDENT << "{const_cast(\"" << func->name() << "\"), (PyCFunction)"; + s << cpythonFunctionName(func) << ", "; + + if (minMax.second < 2) { + if (minMax.first == 0) + s << "METH_NOARGS"; + if (minMax.first != minMax.second) + s << '|'; + if (minMax.second == 1) + s << "METH_O"; + } else { + s << "METH_VARARGS"; + } + if (func->ownerClass() && func->isStatic()) + s << "|METH_STATIC"; + s << "}," << endl; +} + +void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + QString addFunction; + if (cppEnum->enclosingClass()) { + addFunction = QString("PyDict_SetItemString(Py") + + cppEnum->enclosingClass()->name() + + "_Type.tp_dict,"; + } else { + addFunction = "PyModule_AddObject(module,"; + } + + s << INDENT << "// init enum class: " << cppEnum->name() << endl; + + s << INDENT << "if (PyType_Ready(&" << cpythonName << "_Type) < 0)" << endl; + s << INDENT << INDENT << "return;" << endl; + + s << INDENT << "Py_INCREF(&" << cpythonName << "_Type);" << endl; + + s << INDENT << addFunction << endl; + s << INDENT << INDENT << INDENT << "const_cast(\""; + s << cppEnum->name() << "\"), ((PyObject*)&" << cpythonName; + s << "_Type));" << endl; + + foreach (const AbstractMetaEnumValue* enumValue, cppEnum->values()) { + if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name())) + continue; + + s << INDENT << "enum_item = Shiboken::PyEnumObject_New(&"; + s << cpythonName << "_Type," << endl; + s << INDENT << INDENT << INDENT << "const_cast(\""; + s << enumValue->name() << "\"), (long) "; + if (cppEnum->enclosingClass()) + s << cppEnum->enclosingClass()->qualifiedCppName() << "::"; + s << enumValue->name() << ");" << endl; + + s << INDENT << addFunction << endl; + s << INDENT << INDENT << INDENT << "const_cast(\""; + s << enumValue->name() << "\"), enum_item);" << endl; + } + s << endl; +} + +void CppGenerator::writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + + s << "static PyObject*" << endl; + s << cpythonName << "_New(PyTypeObject *type, PyObject *args, PyObject *kwds)" << endl; + s << '{' << endl; + + s << INDENT << "if (!PyType_IsSubtype(type, &" << cpythonName << "_Type))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + + s << INDENT << "PyStringObject* item_name;" << endl; + s << INDENT << "int item_value;" << endl; + + s << INDENT << "if (!PyArg_ParseTuple(args, \"Si:__new__\", &item_name, &item_value))" << endl; + s << INDENT << INDENT << "return 0;" << endl << endl; + + s << INDENT << "PyObject* self = Shiboken::PyEnumObject_New"; + s << "(type, (PyObject*) item_name, item_value);" << endl; + + s << endl << INDENT << "if (!self)" << endl << INDENT << INDENT << "return 0;" << endl; + + s << INDENT << "return self;" << endl << '}' << endl; +} + +void CppGenerator::writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* cppEnum) +{ + QString cpythonName = cpythonEnumName(cppEnum); + QString newFunc; + + if (cppEnum->typeEntry()->isExtensible()) { + newFunc = QString("(newfunc)") + cpythonName + "_New"; + writeEnumNewMethod(s, cppEnum); + s << endl; + } else { + newFunc = "Shiboken::PyEnumObject_NonExtensibleNew"; + } + + s << "static PyGetSetDef " << cpythonName << "_getsetlist[] = {" << endl; + s << INDENT << "{const_cast(\"name\"), (getter)Shiboken::PyEnumObject_name}," << endl; + s << INDENT << "{0} // Sentinel" << endl; + s << "};" << endl << endl; + + s << "PyTypeObject " << cpythonName << "_Type = {" << endl; + s << INDENT << "PyObject_HEAD_INIT(&PyType_Type)" << endl; + s << INDENT << "/*ob_size*/ 0," << endl; + s << INDENT << "/*tp_name*/ const_cast(\"" << cppEnum->name() << "\")," << endl; + s << INDENT << "/*tp_basicsize*/ sizeof(Shiboken::PyEnumObject)," << endl; + s << INDENT << "/*tp_itemsize*/ 0," << endl; + s << INDENT << "/*tp_dealloc*/ 0," << endl; + s << INDENT << "/*tp_print*/ 0," << endl; + s << INDENT << "/*tp_getattr*/ 0," << endl; + s << INDENT << "/*tp_setattr*/ 0," << endl; + s << INDENT << "/*tp_compare*/ 0," << endl; + s << INDENT << "/*tp_repr*/ Shiboken::PyEnumObject_repr," << endl; + s << INDENT << "/*tp_as_number*/ 0," << endl; + s << INDENT << "/*tp_as_sequence*/ 0," << endl; + s << INDENT << "/*tp_as_mapping*/ 0," << endl; + s << INDENT << "/*tp_hash*/ 0," << endl; + s << INDENT << "/*tp_call*/ 0," << endl; + s << INDENT << "/*tp_str*/ Shiboken::PyEnumObject_repr," << endl; + s << INDENT << "/*tp_getattro*/ 0," << endl; + s << INDENT << "/*tp_setattro*/ 0," << endl; + s << INDENT << "/*tp_as_buffer*/ 0," << endl; + s << INDENT << "/*tp_flags*/ Py_TPFLAGS_DEFAULT," << endl; + s << INDENT << "/*tp_doc*/ 0," << endl; + s << INDENT << "/*tp_traverse*/ 0," << endl; + s << INDENT << "/*tp_clear*/ 0," << endl; + s << INDENT << "/*tp_richcompare*/ 0," << endl; + s << INDENT << "/*tp_weaklistoffset*/ 0," << endl; + s << INDENT << "/*tp_iter*/ 0," << endl; + s << INDENT << "/*tp_iternext*/ 0," << endl; + s << INDENT << "/*tp_methods*/ 0," << endl; + s << INDENT << "/*tp_members*/ 0," << endl; + s << INDENT << "/*tp_getset*/ " << cpythonName << "_getsetlist," << endl; + s << INDENT << "/*tp_base*/ &PyInt_Type," << endl; + s << INDENT << "/*tp_dict*/ 0," << endl; + s << INDENT << "/*tp_descr_get*/ 0," << endl; + s << INDENT << "/*tp_descr_set*/ 0," << endl; + s << INDENT << "/*tp_dictoffset*/ 0," << endl; + s << INDENT << "/*tp_init*/ 0," << endl; + s << INDENT << "/*tp_alloc*/ 0," << endl; + s << INDENT << "/*tp_new*/ " << newFunc << ',' << endl; + s << INDENT << "/*tp_free*/ 0," << endl; + s << INDENT << "/*tp_is_gc*/ 0," << endl; + s << INDENT << "/*tp_bases*/ 0," << endl; + s << INDENT << "/*tp_mro*/ 0," << endl; + s << INDENT << "/*tp_cache*/ 0," << endl; + s << INDENT << "/*tp_subclasses*/ 0," << endl; + s << INDENT << "/*tp_weaklist*/ 0" << endl; + s << "};" << endl << endl; +} + +void CppGenerator::writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass) +{ + QString pyTypeName = "Py" + metaClass->name() + "_Type"; + s << "PyAPI_FUNC(void)" << endl; + s << "init_" << metaClass->name().toLower() << "(PyObject *module)" << endl; + s << '{' << endl; + s << INDENT << "if (PyType_Ready(&" << pyTypeName << ") < 0)" << endl; + s << INDENT << INDENT << "return;" << endl << endl; + s << INDENT << "Py_INCREF(&" << pyTypeName << ");" << endl; + s << INDENT << "PyModule_AddObject(module, const_cast(\""; + s << metaClass->name() << "\")," << endl; + s << INDENT << INDENT << "((PyObject*)&" << pyTypeName << "));" << endl << endl; + + if (!metaClass->enums().isEmpty()) { + s << INDENT << "// Initialize enums" << endl; + s << INDENT << "PyObject* enum_item;" << endl << endl; + } + foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) + writeEnumInitialization(s, cppEnum); + + s << '}' << endl << endl; +} + +void CppGenerator::finishGeneration() +{ + //Generate CPython wrapper file + QString classInitDecl; + QTextStream s_classInitDecl(&classInitDecl); + QString classPythonDefines; + QTextStream s_classPythonDefines(&classPythonDefines); + QString namespaceDefines; + QTextStream s_namespaceDefines(&namespaceDefines); + + QSet includes; + QString globalFunctionImpl; + QTextStream s_globalFunctionImpl(&globalFunctionImpl); + QString globalFunctionDecl; + QTextStream s_globalFunctionDef(&globalFunctionDecl); + + Indentation indent(INDENT); + + foreach (AbstractMetaFunctionList globalOverloads, filterGroupedFunctions()) { + AbstractMetaFunctionList overloads; + foreach (AbstractMetaFunction* func, globalOverloads) { + if (!func->isModifiedRemoved()) + overloads.append(func); + } + + if (overloads.isEmpty()) + continue; + + writeMethodWrapper(s_globalFunctionImpl, overloads); + writeMethodDefinition(s_globalFunctionDef, overloads); + } + + foreach (const AbstractMetaClass *cls, classes()) { + if (!shouldGenerate(cls) || cls->enclosingClass()) + continue; + + if (m_packageName.isEmpty()) + m_packageName = cls->package(); + + s_classInitDecl << "extern \"C\" PyAPI_FUNC(void) init_" + << cls->name().toLower() << "(PyObject* module);" << endl; + + QString defineStr = "init_" + cls->name().toLower() + "(module);"; + if (cls->isNamespace()) + s_namespaceDefines << INDENT << defineStr << endl; + else + s_classPythonDefines << INDENT << defineStr << endl; + } + + QString moduleFileName(outputDirectory() + "/" + subDirectoryForPackage(m_packageName)); + moduleFileName += "/" + moduleName().toLower() + "_module_wrapper.cpp"; + + QFile file(moduleFileName); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + + // write license comment + s << licenseComment() << endl; + + s << "#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */" << endl; + s << "#define PyMODINIT_FUNC void" << endl << "#endif" << endl << endl; + + s << "#include " << endl; + s << "#include " << endl; + s << "#include \"" << moduleName().toLower() << "_python.h\"" << endl << endl; + foreach (const QString& include, includes) + s << "#include <" << include << ">" << endl; + s << endl; + + s << "// Global functions "; + s << "------------------------------------------------------------" << endl; + s << globalFunctionImpl << endl; + + s << "static PyMethodDef " << moduleName() << "_methods[] = {" << endl; + s << globalFunctionDecl; + s << INDENT << "{0} // Sentinel" << endl << "};" << endl << endl; + + s << "// Classes initialization functions "; + s << "------------------------------------------------------------" << endl; + s << classInitDecl << endl; + + if (!globalEnums().isEmpty()) { + s << "// Enum definitions "; + s << "------------------------------------------------------------" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) { + writeEnumDefinition(s, cppEnum); + s << endl; + } + } + + s << "// Module initialization "; + s << "------------------------------------------------------------" << endl; + s << "extern \"C\" {" << endl << endl; + + s << "PyMODINIT_FUNC" << endl << "init" << moduleName() << "()" << endl; + s << '{' << endl; + + foreach (const QString& requiredModule, TypeDatabase::instance()->requiredTargetImports()) { + s << INDENT << "if (PyImport_ImportModule(\"" << requiredModule << "\") == NULL) {" << endl; + s << INDENT << INDENT << "PyErr_SetString(PyExc_ImportError," << "\"could not import "; + s << requiredModule << "\");" << endl << INDENT << INDENT << "return;" << endl; + s << INDENT << "}" << endl << endl; + } + + s << INDENT << "PyObject* module = Py_InitModule(\"" << moduleName() << "\", "; + s << moduleName() << "_methods);" << endl << endl; + + s << INDENT << "// Initialize classes in the type system" << endl; + s << classPythonDefines << endl; + + s << INDENT << "// Initialize namespaces as uninstantiable classes in the type system" << endl; + s << namespaceDefines << endl; + + if (!globalEnums().isEmpty()) { + s << INDENT << "// Initialize enums" << endl; + s << INDENT << "PyObject* enum_item;" << endl << endl; + } + + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) + writeEnumInitialization(s, cppEnum); + + s << INDENT << "if (PyErr_Occurred())" << endl; + s << INDENT << INDENT << "Py_FatalError(\"can't initialize module "; + s << moduleName() << "\");" << endl << '}' << endl << endl; + s << "} // extern \"C\"" << endl << endl; + } +} diff --git a/cppgenerator.h b/cppgenerator.h new file mode 100644 index 000000000..79ebc19a4 --- /dev/null +++ b/cppgenerator.h @@ -0,0 +1,80 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef CPPGENERATOR_H +#define CPPGENERATOR_H + +#include "shibokengenerator.h" +#include "polymorphicdata.h" + +/** + * The CppGenerator generate the implementations of C++ bindings classes. + */ +class CppGenerator : public ShibokenGenerator +{ +public: + void setDisableNamedArgs(bool disable) { m_disableNamedArgs = disable; } + +protected: + QString fileNameForClass(const AbstractMetaClass* metaClass) const; + QList filterGroupedFunctions(const AbstractMetaClass* metaClass = 0); + QList filterGroupedOperatorFunctions(const AbstractMetaClass* metaClass, + uint query); + QString cpythonWrapperCPtr(const AbstractMetaClass* metaClass, QString argName = "self"); + + void generateClass(QTextStream& s, const AbstractMetaClass* metaClass); + void finishGeneration(); + +private: + void writeNonVirtualModifiedFunctionNative(QTextStream& s, const AbstractMetaFunction* func); + void writeConstructorNative(QTextStream& s, const AbstractMetaFunction* func); + void writeDestructorNative(QTextStream& s, const AbstractMetaClass* metaClass); + void writeVirtualMethodNative(QTextStream& s, const AbstractMetaFunction* func); + + void writeConstructorWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); + void writeDestructorWrapper(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMethodWrapper(QTextStream &s, const AbstractMetaFunctionList overloads); + void writeArgumentsInitializer(QTextStream& s, PolymorphicData& polymorphicData); + + void writeErrorSection(QTextStream& s, PolymorphicData& polymorphicData); + void writeTypeCheck(QTextStream& s, const PolymorphicData* polyData, QString argumentName); + + void writePolymorphicDecisor(QTextStream& s, PolymorphicData* parentPolymorphicData); + void writeMethodCall(QTextStream& s, const AbstractMetaFunction* func, int lastArg = 0); + + void writeClassRegister(QTextStream& s, const AbstractMetaClass* metaClass); + void writeClassDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + void writeMethodDefinition(QTextStream& s, const AbstractMetaFunctionList overloads); + void writeTypeAsNumberDefinition(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeRichCompareFunction(QTextStream& s, const AbstractMetaClass* metaClass); + + void writeEnumNewMethod(QTextStream& s, const AbstractMetaEnum* metaEnum); + void writeEnumDefinition(QTextStream& s, const AbstractMetaEnum* metaEnum); + void writeEnumInitialization(QTextStream& s, const AbstractMetaEnum* metaEnum); + + bool m_disableNamedArgs; +}; + +#endif // CPPGENERATOR_H + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 000000000..b8f9d4895 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +shiboken (0.2) unstable; urgency=low + + * Initial release + + -- Marcelo Lira Sat, 30 May 2009 16:16:00 -0300 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 000000000..7ed6ff82d --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 000000000..e7fb555a8 --- /dev/null +++ b/debian/control @@ -0,0 +1,25 @@ +Source: shiboken +Section: devel +Priority: extra +Maintainer: Marcelo Lira +Build-Depends: cdbs, debhelper (>= 5), libapiextractor-dev (>= 0.2), libqt4-dev (>= 4.5), cmake (>= 2.6.0) +Standards-Version: 3.7.3 + +Package: shiboken +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libapiextractor (>= 0.2) +Description: Generates Python bindings for qt based libraries. + Generates CPython based bindings for C++ libraries. + +Package: libshiboken +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Support library for Python bindings created with Shiboken generator. + Support library for Python bindings created with Shiboken generator. + +Package: libshiboken-dev +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libshiboken +Description: Development files for libshiboken. + Development files for libshiboken. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 000000000..bef1d32de --- /dev/null +++ b/debian/copyright @@ -0,0 +1,37 @@ +This package was debianized based on previous debianization by Marcelo Lira on +Sat, 30 May 2009 16:16:00 -0300. + +Upstream Authors: + + Hugo Lima + Luciano Wolf + Marcelo Lira + Renato Araujo + +Copyright: + + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(ies) + +License: + + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + On Debian systems, the complete text of the GNU General Public License + can be found in `/usr/share/common-licenses/GPL'. + + +The Debian packaging is (C) 2009, Nokia Corporation and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + diff --git a/debian/rules b/debian/rules new file mode 100755 index 000000000..1e6acdd00 --- /dev/null +++ b/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/cmake.mk + + +# Add here any variable or target overrides you need. + +DEB_CMAKE_EXTRA_FLAGS=-DCMAKE_MODULE_PATH=/usr/share/cmake-2.6/Modules + +# borrowed from python2.5 debian/rules +COMMA = , +ifneq (,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS)))) + DEB_MAKE_ENVVARS := MAKEFLAGS=-j$(subst parallel=,,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS)))) +endif diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 000000000..69b857c58 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,88 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf _build/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + @echo + @echo "Build finished. The HTML pages are in _build/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml + @echo + @echo "Build finished. The HTML pages are in _build/dirhtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in _build/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in _build/qthelp, like this:" + @echo "# qcollectiongenerator _build/qthelp/PyQtB.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile _build/qthelp/PyQtB.qhc" + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo + @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + @echo + @echo "The overview file is in _build/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in _build/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in _build/doctest/output.txt." diff --git a/doc/_static/.gitignore b/doc/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/doc/_templates/index.html b/doc/_templates/index.html new file mode 100644 index 000000000..f1a289077 --- /dev/null +++ b/doc/_templates/index.html @@ -0,0 +1,34 @@ +{% extends "layout.html" %} +{% set title = 'Overview' %} +{% block body %} +

{{ project }} {{ version }}

+ +

{{ project }} is a tool that eases the development of Python bindings for Qt-based libraries by + automating most of the process. It relies heavily on the ApiExtractor library to parse the + header files and manipulate the classes information while generating the code. This generated + code uses the + Boost::Python library + in order to bridge the C++ library and Python.

+ +

{{ project }} is based on the + QtScriptGenerator project.

+ +

Documentation

+ + + +
+ + + + + + +
+ +{% endblock %} diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html new file mode 100644 index 000000000..f15583b4d --- /dev/null +++ b/doc/_templates/layout.html @@ -0,0 +1,10 @@ +{% extends "!layout.html" %} +{% block rootrellink %} + +
  • {{ shorttitle }}{{ reldelim1 }}
  • +{% endblock %} +{% block extrahead %} + +{{ super() }} +{% endblock %} diff --git a/doc/compiling/cmake-primer.rst b/doc/compiling/cmake-primer.rst new file mode 100644 index 000000000..884cf15c1 --- /dev/null +++ b/doc/compiling/cmake-primer.rst @@ -0,0 +1,68 @@ + +.. _cmake-primer: + +************ +CMake primer +************ + +This chapter will is a basic introduction to CMake, the build system +used by PyQtB and the binding generator. + +The practical steps will be focusing in using cmake in a unix-like +(GNU/Linux) environment. + + +Configuring +=========== + +Project file - CMakeLists.txt +----------------------------- + +CMake parses the file CMakeLists.txt for information about the project, +like project name, dependencies, what should be compiled, what should be +shipped. + + +CMake variables +--------------- + +CMake can have its default behavior modified by providing some + +* ``CMAKE_INSTALL_PREFIX=`` sets the install prefix to + the specified path. +* ``CMAKE_MODULE_PATH=`` sets the extra directories + where CMake will try to find its modules. +* ``CMAKE_TOOLCHAIN_FILE=`` sets the path to the file that + describes the toolchain used to compile this project. Is is very useful + when using CMake with icecc to speedup compilation. + +You can define a variable using the ``-D`` switch. + +Invoking CMake +-------------- + +After writing the CMakeLists.txt and deciding which flags will be used, +you can invoke CMake using:: + + cmake + +For example, if you use the ``build/`` folder to build the project and +want it to be installed into ``/opt/sandbox/``, use the following lines:: + + cd build/ + cmake -DCMAKE_INSTALL_PREFIX=/opt/sandbox .. + +CMake will process the project file and write the output files in the +current directory + +Building +======== + +After the configuration process, the Makefiles are written and you can build +the project using :program:`make`. + +Installing +========== + +As in the building process, ``make install`` will install the files into +the target directory. diff --git a/doc/compiling/compiling.rst b/doc/compiling/compiling.rst new file mode 100644 index 000000000..638efa91a --- /dev/null +++ b/doc/compiling/compiling.rst @@ -0,0 +1,9 @@ +Compiling +********* + +.. toctree:: + :maxdepth: 3 + + cmake-primer + setup-apiextractor + setup-generator diff --git a/doc/compiling/setup-apiextractor.rst b/doc/compiling/setup-apiextractor.rst new file mode 100644 index 000000000..68c162f8b --- /dev/null +++ b/doc/compiling/setup-apiextractor.rst @@ -0,0 +1,56 @@ + +.. _api-extractor: + +************** +API Extractor +************** + +Overview +======== + +The **API Extractor** library is used by the binding generator to +parse the header and typesystem files to create an internal +representation of the API. It is based on the QtScriptGenerator +codebase. + +Getting the sources +=================== + +.. todo:: + Replace with the OSS project repo info + +* Git URL: https://dvcs.projects.maemo.org/git/python/apiextractor +* Web interface: https://dvcs.projects.maemo.org/git/?p=python/apiextractor + +To checkout the most updated version, use the following command:: + + $ git clone https://dvcs.projects.maemo.org/git/python/apiextractor/ + +Build requirements +================== + +* Qt4.5 development headers and libraries >= 4.5.0 +* libboost-graph >= 1.38.0 +* cmake >= 2.6.0 + +Building and installing +======================= + +To build and install just follow the generic cmake instructions in section +:ref:`cmake-primer`. + +Debian packaging +================ + +In order to compile this package in a debian environment, make sure the +following packages are installed: + +* debhelper (>= 5) +* cdbs +* cmake (>= 2.6.0) +* libboost-graph1.38-dev (>= 1.38.0) +* libqt4-dev (>= 4.5) + +And then you can build the package using:: + + $ dpkg-buildpackage -rfakeroot diff --git a/doc/compiling/setup-generator.rst b/doc/compiling/setup-generator.rst new file mode 100644 index 000000000..28fe2fbb3 --- /dev/null +++ b/doc/compiling/setup-generator.rst @@ -0,0 +1,54 @@ + +.. _python-bindings-generator: + +************************* +Python Bindings Generator +************************* + +Overview +========================================= + +The **Python Bindings Generator** (A.K.A. :program:`shiboken`) is +the program that creates the bindings source files from Qt headers and +auxiliary files (typesystems, ``global.h`` and glue files). It makes +heavy use of the :ref:`api-extractor` library. + + +Getting the sources +=================== + +* Git URL: https://dvcs.projects.maemo.org/git/python/shiboken +* Web interface: https://dvcs.projects.maemo.org/git/?p=python/shiboken + +To checkout the most updated version, use the following command:: + + $ git svn clone https://dvcs.projects.maemo.org/git/?p=python/shiboken + +Build requirements +================== + ++ CMake >= 2.6.0 ++ Qt4.5 libraries and development headers >= 4.5.0 ++ :ref:`api-extractor` + development headers + +Building and installing +======================= + +To build and install just follow the generic cmake instructions in +section :ref:`cmake-primer`. + +Debian packaging +================ + +In order to compile this package in a debian environment, make sure the +following packages are installed: + +* debhelper (>= 5) +* cdbs +* cmake (>= 2.6.0) +* libqt4-dev (>= 4.5) +* libapiextractor-dev (>= 0.1) + +And then you can build the package using:: + + $ dpkg-buildpackage -rfakeroot diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 000000000..6862c28d0 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +# +# PyQtB documentation build configuration file, created by +# sphinx-quickstart on Wed Apr 22 15:04:20 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.ifconfig', 'sphinx.ext.refcounting', 'sphinx.ext.coverage'] + +rst_epilog = """ +.. |project| replace:: Shiboken +""" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +source_encoding = 'utf-8' + +# The master toctree document. +#master_doc = 'contents' + +# General information about the project. +project = u'Shiboken' +copyright = u'2009, Nokia Corporation' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = { +#} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = { '' : ''} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +html_additional_pages = { 'index' : 'index.html'} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +html_use_index = True + +# If true, the index is split into individual pages for each letter. +html_split_index = False + +# If true, links to the reST sources are added to the pages. +html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +#htmlhelp_basename = 'PyQtBdoc' + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'PyQtB.tex', u'Brian Documentation', + u'Nokia Corporation', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/doc/contents.rst b/doc/contents.rst new file mode 100644 index 000000000..31d16dc9d --- /dev/null +++ b/doc/contents.rst @@ -0,0 +1,10 @@ +Table of contents +***************** +.. toctree:: + :numbered: + :maxdepth: 3 + + overview.rst + faq.rst + tutorial/introduction.rst + compiling/compiling.rst diff --git a/doc/dependency-pyqtb.svg b/doc/dependency-pyqtb.svg new file mode 100644 index 000000000..a458bf6fb --- /dev/null +++ b/doc/dependency-pyqtb.svg @@ -0,0 +1,600 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + API Extractor + 0.2 + Headers and libraries - compile-time + LGPL version 2.1 + + + + + + Shiboken (generator) + 0.1 + Binary executable - compile-time + GPL version 2 + + + + + + boost::graph + 1.38.0 + headers and libraries - compile-time + Boost Software License 1.0 + + + + + + + + + Boost + + + + Qt Software + + + + INdT/Nokia + + + + Python Foundation + + + + + + + + libshiboken + 0.1 + Headers and libraries - compile-time + LGPL version 2.1 + + + + + + Qt 4.5 + 4.5 + headers and libraries - compile-time and run-time + GNU General Public License v3 /GNU Lesser General Public Licence v2.1 + + + + + + Python + 2.6 + Headers and libraries - compile-time and run-time + Python license + + + + + + diff --git a/doc/faq.rst b/doc/faq.rst new file mode 100644 index 000000000..38cc3f186 --- /dev/null +++ b/doc/faq.rst @@ -0,0 +1,107 @@ +************************** +Frequently Asked Questions +************************** + +This is a list of Frequently Asked Questions about |project|. Feel free to +suggest new entries! + +General +======= + +What is the generator? +---------------------- + +Here the name generator refers actually to a program composed of a set of +*generator classes* that output different resources based on information +contained inside C++ header files. + +What is the API Extractor? +-------------------------- + +It is a library that parses C++ header files and builds a data model around +them in order to ease the work of manipulating these data inside +*generators*. + + +Is there any similar tools around? +---------------------------------- + +The generator framework actually started as a fork of the qtscriptgenerator, +with the focus on python bindings instead of QtScript. After some time, the +python-specific code was split from the the header parsing and data model +code. The former became what we call *generator* while the latter is now +called *API Extractor*. + +What's the relationship between the generator and the API Extractor? +-------------------------------------------------------------------- + +The generator system relies heavily in the API Extractor classes, using +them as syntatic sugar to access the data model of the classes being +wrapped. + +What are the dependencies to run the generator? +----------------------------------------------- + +API Extractor, QtCore and QtXml. + +Creating bindings +================= + +Can I wrap non-Qt libraries? +---------------------------- + +Although it's not yet well tested, there's a good chance that non-Qt +libraries can be wrapped using the generator. But remember that +generator injects runtime dependency on Qt for the generated binding. + +Is there any runtime dependency on the generated binding? +--------------------------------------------------------- + +Yes. Only libshiboken, and the obvious Python interpreter +and the C++ library that is being wrapped. + +What do I have to do to create my bindings? +------------------------------------------- + +.. todo: put link to typesystem documentation + +Most of the work is already done by the API Extractor. The developer creates +a typesystem file with any customization wanted in the generated code, like +removing classes or changing method signatures. The generator will output +the .h and .cpp files with the CPython code that will wrap the target +library for python. + +Is there any recommended build system? +-------------------------------------- + +Both API Extractor and generator uses and recommends the CMake build system. + +Can I write closed-source bindings with the generator? +------------------------------------------------------ + +Yes, as long as you use a LGPL version of Qt, due to runtime requirements. + +What is 'inject code'? +---------------------- + +That's how we call customized code that will be *injected* into the +generated at specific locations. They are specified inside the typesytem. + +How can I document my project? +------------------------------ + +The generator also can generate the API documentation based on the +C++ headers documentation using the qdoc syntax. Optionally you can +inject documentation at specific parts. Likewise *inject code*, the +customized documentation is specified inside the typesystem. + +Other +===== + +Is there any current limitation within the generator/API Extractor? +------------------------------------------------------------------- + +The generator currently does not automatically detects implicit C++ +type conversions. Also the code snippets in function signature and +examples are still in C++ inside the generated documentation. + diff --git a/doc/images/.directory b/doc/images/.directory new file mode 100644 index 000000000..e65475f65 --- /dev/null +++ b/doc/images/.directory @@ -0,0 +1,3 @@ +[Dolphin] +ShowPreview=true +Timestamp=2009,5,5,17,43,26 diff --git a/doc/images/bindinggen-development.png b/doc/images/bindinggen-development.png new file mode 100644 index 000000000..2dd64ba1d Binary files /dev/null and b/doc/images/bindinggen-development.png differ diff --git a/doc/images/boostgen.png b/doc/images/boostgen.png new file mode 100644 index 000000000..ae9d9fc3d Binary files /dev/null and b/doc/images/boostgen.png differ diff --git a/doc/images/boostqtarch.png b/doc/images/boostqtarch.png new file mode 100644 index 000000000..f1b145e9c Binary files /dev/null and b/doc/images/boostqtarch.png differ diff --git a/doc/overview.rst b/doc/overview.rst new file mode 100644 index 000000000..2ceb42fb8 --- /dev/null +++ b/doc/overview.rst @@ -0,0 +1,46 @@ +.. _gen-overview: + +****************** +Generator Overview +****************** + +In a few words, the Generator is a system that +parses a collecion of header and typesystem files, generating other +files (code, documentation, etc.) as result. + +Creating new bindings +===================== + +.. figure:: images/bindinggen-development.png + :scale: 80 + :align: center + + Creating new bindings + +Each module of the generator system has a specific role. + +1. Provide enough data about the classes and functions. +2. Generate valid code, with modifications from typesystems and + injected codes. +3. Modify the API to expose the objects in a Python-friendly way. +4. Insert customizations where handwritten code is needed. + +.. figure:: images/boostqtarch.png + :scale: 80 + :align: center + + Runtime architecture + +Handwritten inputs +================== + +Creating new bindings involves creating two pieces of "code": +The typesystem and the inject code. + +:typesystem: XML files that provides the developer with a tool to + customize the way that the generators will see the classes + and functions. For example, functions can be renamed, have + its signature changed and many other actions. +:inject code: allows the developer to insert handwritten code where + the generated code is not suitable or needs some customization. + diff --git a/doc/tutorial/bindinglibfoo.rst b/doc/tutorial/bindinglibfoo.rst new file mode 100644 index 000000000..fc256fbaa --- /dev/null +++ b/doc/tutorial/bindinglibfoo.rst @@ -0,0 +1,76 @@ +.. highlight:: xml + +.. _gentut-bindinglibfoo: + +Binding libfoo with the Generator +================================= + +In order to create bindings for a library based on Qt4 a number of components +must be available in the system. + + + Qt4 library (with headers and pkg-config .pc files for development -- the + ``-dev`` packages in a Debian distribution). + + Qt4 Python bindings made with **boostgenerator**. + + Typesystems for the Qt4 Python bindings. + + Headers for the library to be bound. + +With the above listed in place the developer must write the components from +where the generator will gather information to create the binding source code. + + + Typesystem file describing the way the binding must be done. + + **global.h** including all the **libfoo** headers and defining required macros. + + A build system to direct the process of generating, compiling and linking the binding. + +The directory structure for the binding project could be something like the tree +shown below: + +:: + + foobinding/ + |-- data/ + `-- module_dir/ + `-- glue/ + + +The **data** directory should contain the **global.h** and the typesystem +file. This typesystem need to refer to the ones used to create the Qt4 bindings, +commonly located on **/usr/share/PyQtB/typesystem**, the exact location +can be checked with pkg-config: + +:: + + $ pkg-config qt4python --variable=typesystemdir + + +The **module_dir** directory is the place where the sources generated should +be placed. It starts empty except for the build instructions file (Makefile, +Makefile.am, CMakeLists.txt, etc). The realname of this directory must be the +same written in the typesystem file: + +:: + + + + +If there is any need for handwritten source code longer than a couple of lines, +making them unconfortable to be put on the typesystem xml file, the sources +could be orderly placed in a **glue** directory, also referred in the +new binding typesystem. + +When writing the typesystem file (more on this later) there is no need to refer +to the other required typesystem files with absolute paths, the locations where +they can be found could be passed to the generator through a command line +option (``--typesystem-paths=PATH1:PATH2:[...]``) or the environment variable +**TYPESYSTEMPATH**. + +For **libfoo** no glue code will be needed so this directory is not used, +the other directories are created with proper names. + +:: + + foobinding/ + |-- data/global.h + | `-- typesystem_foo.xml + `-- foo/ + `-- Makefile + diff --git a/doc/tutorial/buildingthebinding.rst b/doc/tutorial/buildingthebinding.rst new file mode 100644 index 000000000..bc60fe284 --- /dev/null +++ b/doc/tutorial/buildingthebinding.rst @@ -0,0 +1,133 @@ +.. _gentut-buildingthebinding: + +Building The Binding +==================== + +As mentioned before the build system used must perform the following tasks +in the correct order: + + + Gather data about locations of headers and external needed typesystems. + + Run the generator with the correct parameters. + + Compile and link the binding. + +The first and last are the usual, being the second the only novelty in the +process. + +Running the Generator +--------------------- + +The generator is called with the following parameters and options: + +:: + + $ boostgenerator global_headers.h \ + --include-paths=$(PATHS_TO_HEADERS)) \ + --typesystem-paths=$(PATHS_TO_TYPESYSTEMS) \ + --output-directory=. \ + typesystem.xml + +Notice that the variables for include and typesystem paths could be determined +at build time with the pkg-config tool. + +Collecting information with pkg-config +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Qt4 bindings include compile and build information through the pkg-config +mechanism. The pkg-config name for Qt4 Python bindings is **qt4python** and a +simple ``pkg-config qt4python --cflags --libs`` will retrieve the information +needed to build the new binding. + +The Qt4 bindings file ``qt4python.pc`` for the use of pkg-config requires +the ``.pc`` files from Qt4 to be installed. If the library is in an unusual +location, e.g. ``/opt/qt45``, remember to export it to the ``PKG_CONFIG_PATH`` +environment variable. +For example: ``export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/qt45/lib/pkgconfig`` + +There is a vital information also available through pkg-config: +the **typesystemdir** variable. It is used like this: +``pkg-config qt4python --variable=typesystemdir`` This provides information +where to find the typesystem files used to create the Qt4 bindings, and as said +before the binding being created needs this to complement its own binding +information for the generation proccess. + +Makefile +-------- + +Below is a plain Makefile for the binding project. + +**foobinding/foo/Makefile** +:: + + LIBTEST_DIR = ../../libfoo + LIBS = -lboost_python-gcc43-1_38-py25 -lpython2.5 \ + `pkg-config qt4python --libs` \ + -lfoo -L$(LIBTEST_DIR) \ + -lpthread -ldl -lutil + CFLAGS = -I/usr/share/qt4/mkspecs/linux-g++ -I. \ + -I$(LIBTEST_DIR) \ + `pkg-config qt4python --cflags` \ + -I/usr/include/python2.5\ + -I/usr/include/boost/python + QT4TYPESYSTEM_DIR = `pkg-config --variable=typesystemdir qt4python` + QT4HEADER_DIRS = `pkg-config --variable=includedir QtCore`:`pkg-config --variable=includedir QtCore`/.. + + SOURCES = math_wrapper.cpp foo_module_wrapper.cpp foo_global_functions_wrapper.cpp + OBJECTS = math_wrapper.o foo_module_wrapper.o foo_global_functions_wrapper.o + + all: generate compile link + + generate: + boostgenerator ../data/global.h \ + --include-paths=$(LIBTEST_DIR):$(QT4HEADER_DIRS):/usr/include \ + --typesystem-paths=../data:$(QT4TYPESYSTEM_DIR) \ + --output-directory=.. \ + ../data/typesystem_foo.xml + + compile: $(SOURCES) + g++ -fPIC -DPIC $(CFLAGS) math_wrapper.cpp -c + g++ -fPIC -DPIC $(CFLAGS) foo_global_functions_wrapper.cpp -c + g++ -fPIC -DPIC $(CFLAGS) foo_module_wrapper.cpp -c + + link: + g++ -shared -Wl,-soname,foo.so -o foo.so $(LIBS) $(OBJECTS) + + test: + LD_LIBRARY_PATH=$(LIBTEST_DIR) python -c \ + "import foo; print dir(foo); m = foo.Math(); print m.squared(5)" + + clean: + rm -rf *.o *.so *.?pp *.log + + +Keepe in mind that the Makefile above expects the ``libfoo`` and +``foobinding`` directories to be in the same level in the directory +hierarchy, remember to change any path references accordingly if +you choose to change things. + +**Warning:** + The order in which the link flags are passed matters. + **libboost_python** must come first, otherwise weeping + and gnashing of teeth will follow. + +Testing the Binding +------------------- +Now compile the binding with ``make``: + +:: + + $ cd foobinding/foo + $ make + +To test if the new binding is working (it can pass the build phase but still +blow up at runtime) start up a Python terminal and import it by the name. + +:: + + $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/libfoo/shared/object/dir + $ export PYTHONPATH=$PYTHONPATH:/path/to/foo/python/module/file/dir + $ python + >> import foo + >> print dir(foo) + >> m = foo.Math() + >> print m.squared(5) + diff --git a/doc/tutorial/globalheader.rst b/doc/tutorial/globalheader.rst new file mode 100644 index 000000000..d1ac2392e --- /dev/null +++ b/doc/tutorial/globalheader.rst @@ -0,0 +1,36 @@ +.. highlight:: cpp + +.. _gentut-globalheader: + +The Global Header +================= + +Besides the information provided by the typesystem, the generator needs to +gather more data from the library headers containing the classes to be exposed +in Python. If there is a header that include all the others (or just one, as is +the case of **libfoo**) this could be passed directly to the generator. + +If such a file is not available, or only a subset of the library is desired, or +if some flags must be defined before parsing the library headers, then a +``global.h`` file must be provided. + +The use of a ``global.h`` file is preferred if some macros must be defined +before the parser gather data from the headers. For example, if ``NULL`` is not +defined and it is used as a default paramater for some constructor or method, +the parser will not recognize it. + +The solve this create a ``global.h`` including all the desired headers and the +defined (and undefined) flags as follows: + +**foobinding/data/global.h** +:: + + #undef QT_NO_STL + #undef QT_NO_STL_WCHAR + + #ifndef NULL + #define NULL 0 + #endif + + #include + diff --git a/doc/tutorial/images/generatorworkings.png b/doc/tutorial/images/generatorworkings.png new file mode 100644 index 000000000..d35a565ff Binary files /dev/null and b/doc/tutorial/images/generatorworkings.png differ diff --git a/doc/tutorial/images/generatorworkings.svg b/doc/tutorial/images/generatorworkings.svg new file mode 100644 index 000000000..85a7782af --- /dev/null +++ b/doc/tutorial/images/generatorworkings.svg @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + bindingsource code + + + + + + + typesystemdescriptions + + + + customsourcecode + + + + + + libraryheaders + + + + + + Parser + + + + GeneratorBackend + + + + TypeDatabase + + + + + diff --git a/doc/tutorial/introduction.rst b/doc/tutorial/introduction.rst new file mode 100644 index 000000000..5234551b7 --- /dev/null +++ b/doc/tutorial/introduction.rst @@ -0,0 +1,31 @@ +Binding Generation Tutorial +*************************** + +This tutorial intends to describe the process of binding creation with +|project| and using a very simple Qt4 based library will be used as an +example. + +The image below shows the inputs needed to generate the binding source code. + +.. image:: images/generatorworkings.png + +Putting in words, the user provides the headers for the library along with a +typesystem file describing how the classes will be exposed in the target +language, as well as any needed custom source code to be merged with +the generated source code. + +This tutorial will go through the steps needed to have the binding +being able to be imported and used from a Python program. + +**NOTE:** the binding generator is intended to be used with Qt4 based libraries +only, at least for the time being. + +.. toctree:: + :maxdepth: 3 + + libfoo + bindinglibfoo + typesystemcreation + globalheader + buildingthebinding + diff --git a/doc/tutorial/libfoo.rst b/doc/tutorial/libfoo.rst new file mode 100644 index 000000000..217577a4f --- /dev/null +++ b/doc/tutorial/libfoo.rst @@ -0,0 +1,68 @@ +.. highlight:: cpp + +.. _gentut-libfoo: + +Creating the foo library +========================= + +In this section is the code and build instructions for a very simple Qt4 based +library which will serve as a subject for this tutorial. + +The Source Code +--------------- + +There is only one class on this foo library plus a ``.pro`` file which means +that the build system used will be Trolltech's **qmake**. + +Put the files below in a directory called **libfoo**. Be aware that this +directory will be refered by the binding Makefile presented in a next section +of this tutorial. If you want to use other names or paths change the binding +Makefile accordingly. Blind copy'n'paste shortens your life. + +**libfoo/foo.h** +:: + + #ifndef FOO_H + #define FOO_H + + #include + + class Math : public QObject + { + Q_OBJECT + public: + Math() {} + virtual ~Math() {} + int squared(int x); + }; + #endif // FOO_H + + +**libfoo/foo.cpp** +:: + + #include "foo.h" + + int Math::squared(int x) + { + return x * x; + } + + +**libfoo/foo.pro** +:: + + TEMPLATE = lib + TARGET = foo + DEPENDPATH += . + INCLUDEPATH += . + HEADERS += foo.h + SOURCES += foo.cpp + +To build the lib: + +:: + + $ cd libfoo + $ qmake + $ make diff --git a/doc/tutorial/typesystemcreation.rst b/doc/tutorial/typesystemcreation.rst new file mode 100644 index 000000000..f36a8769a --- /dev/null +++ b/doc/tutorial/typesystemcreation.rst @@ -0,0 +1,136 @@ +.. highlight:: xml + +.. _gentut-typesystem: + +Creating the Typesystem Description +=================================== + +The type system is a specification used when mapping a C++ based library onto a +corresponding Python module. The specification is a handwritten XML document +listing the types that will be available in the generated binding, alterations +to classes and function signatures to better suit the target language, +and listing the components that should be rejected for the binding. + +**PyQtB** uses a typesystem format similar to the ones used **QtJambi** and +**QtScript**, thoroughly described in the page *"The Qt Jambi Type System"*. [#]_ + +The divergences between **PyQtB** and QtScript/QtJambi typesystems will be +highlighted whenever they appear. Things to be aware of when writing +a typesystem will be also mentioned. + +Describing **libfoo** for Python Audiences +------------------------------------------ + +All typesystem files start with the root ``typesystem`` tag, the +``package`` attribute carries the name of the package as it will be seen +from Python. + +Right after that, all the typesystem files providing information required for +the generation process are included in the same fashion as header files in C. + +**foobinding/data/typesystem_test.xml** +:: + + + + + + + + +The inclusion of the other typesystem files is achieved with the +``load-typesystem`` tag. The ``generate`` attribute must be set to ``"no"`` +or else the generator will try to create more source code for the already +existing bindings included for reference. + +The C++ classes derived from **QObject** intended to be exposed in the target +language are described with ``object-type`` tags. + + +For this example binding just specifying the name of the class does the trick, +since the generator system will automatically catch the methods with arguments +and return value of types known. These types can be described in the same +typesystem file or in the ones referenced with the ``load-typesystem`` tag. + +In more complex situations method signatures can be changed or rejected with +other tags that can be checked out in the typesystem reference. + + +Other Common Cases and Differences +---------------------------------- + +What follows now is some common uses of the typesystem capabilities. All of them +can be seen in the Qt4 typesystem files. They are not used for this binding +tutorial example, so if you just want to have things working ASAP, +move along. + +Templates +~~~~~~~~~ + +To ease the process of writing custom code for the binding, recurring pieces of +code can be turned generic with the Typesystem template mechanism. +They are declared in a way similar to this snippet: + +:: + + + +And is used as in this example: + +:: + + + + + + +The ``typesystem_template.xml`` file from the Qt4 bindings can be used as a +good resource for examples of this. Check also the QtJambi documentation on +typesystem templates. [#]_ + +Non-QObject Derived Classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Even in a Qt4 based library it is common to find classes that doesn't +pertain to the QObject hierarchy, these must be declared as ``value-type``: + +:: + + + + +Unused Tags +~~~~~~~~~~~ + +Some tags defined in the QtScript/QtJambi typesystem has no effect in **PyQtB** +typesystem, they are: + + + conversion-rule + + argument-map + +Changes to ``"inject-code"`` Tag +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can pass a file name to the **inject-code** tag, the file contents will +be injected in the generated code. + +The ``class`` attribute value ``java`` was changed to ``target``, while +``native`` remained the same. + +Global Functions +~~~~~~~~~~~~~~~~ + +The **BoostGenerator** supports global functions, you can also reject these +functions using the **rejection** tag like is done to reject classes, just +pass an empty string to the class attribute. + +:: + + + + +.. [#] http://doc.trolltech.com/qtjambi-4.4/html/com/trolltech/qt/qtjambi-typesystem.html +.. [#] http://doc.trolltech.com/qtjambi-4.4/html/com/trolltech/qt/qtjambi-typesystem.html#using-code-templates diff --git a/docgenerator.cpp b/docgenerator.cpp new file mode 100644 index 000000000..41b30cd62 --- /dev/null +++ b/docgenerator.cpp @@ -0,0 +1,1370 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "docgenerator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static Indentor INDENT; + +namespace +{ + +static bool functionSort(const AbstractMetaFunction *func1, const AbstractMetaFunction *func2) +{ + return func1->name() < func2->name(); +} + +QString createRepeatedChar(int i, char c) +{ + QString out; + for (int j = 0; j < i; ++j) + out += c; + + return out; +} + +QString escape(QString& str) +{ + return str + .replace("*", "\\*") + .replace("_", "\\_"); +} + +QString escape(const QStringRef& strref) +{ + QString str = strref.toString(); + return escape(str); +} + +} + +QtXmlToSphinx::QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context) + : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) +{ + m_handlerMap.insert("heading", &QtXmlToSphinx::handleHeadingTag); + m_handlerMap.insert("brief", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("para", &QtXmlToSphinx::handleParaTag); + m_handlerMap.insert("italic", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("bold", &QtXmlToSphinx::handleBoldTag); + m_handlerMap.insert("see-also", &QtXmlToSphinx::handleSeeAlsoTag); + m_handlerMap.insert("snippet", &QtXmlToSphinx::handleSnippetTag); + m_handlerMap.insert("dots", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("codeline", &QtXmlToSphinx::handleDotsTag); + m_handlerMap.insert("table", &QtXmlToSphinx::handleTableTag); + m_handlerMap.insert("header", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("row", &QtXmlToSphinx::handleRowTag); + m_handlerMap.insert("item", &QtXmlToSphinx::handleItemTag); + m_handlerMap.insert("argument", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("teletype", &QtXmlToSphinx::handleArgumentTag); + m_handlerMap.insert("link", &QtXmlToSphinx::handleLinkTag); + m_handlerMap.insert("inlineimage", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("image", &QtXmlToSphinx::handleImageTag); + m_handlerMap.insert("list", &QtXmlToSphinx::handleListTag); + m_handlerMap.insert("term", &QtXmlToSphinx::handleTermTag); + m_handlerMap.insert("raw", &QtXmlToSphinx::handleRawTag); + m_handlerMap.insert("underline", &QtXmlToSphinx::handleItalicTag); + m_handlerMap.insert("superscript", &QtXmlToSphinx::handleSuperScriptTag); + m_handlerMap.insert("code", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("legalese", &QtXmlToSphinx::handleCodeTag); + m_handlerMap.insert("section", &QtXmlToSphinx::handleAnchorTag); + m_handlerMap.insert("quotefile", &QtXmlToSphinx::handleQuoteFileTag); + + // ignored tags + m_handlerMap.insert("generatedlist", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("tableofcontents", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("quotefromfile", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("skipto", &QtXmlToSphinx::handleIgnoredTag); + m_handlerMap.insert("target", &QtXmlToSphinx::handleIgnoredTag); + + // useless tags + m_handlerMap.insert("description", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("definition", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("printuntil", &QtXmlToSphinx::handleUselessTag); + m_handlerMap.insert("relation", &QtXmlToSphinx::handleUselessTag); + + m_result = transform(doc); +} + +void QtXmlToSphinx::pushOutputBuffer() +{ + QString* buffer = new QString(); + m_buffers << buffer; + m_output.setString(buffer); +} + +QString QtXmlToSphinx::popOutputBuffer() +{ + Q_ASSERT(!m_buffers.isEmpty()); + QString* str = m_buffers.pop(); + QString strcpy(*str); + delete str; + m_output.setString(m_buffers.isEmpty() ? 0 : m_buffers.top()); + return strcpy; +} + + +QString QtXmlToSphinx::transform(const QString& doc) +{ + Q_ASSERT(m_buffers.isEmpty()); + Indentation indentation(INDENT); + if (doc.trimmed().isEmpty()) + return doc; + + pushOutputBuffer(); + + QXmlStreamReader reader(doc); + + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (reader.hasError()) { + m_output << INDENT << "XML Error: " + reader.errorString() + "\n" + doc; + ReportHandler::warning("XML Error: " + reader.errorString() + "\n" + doc); + break; + } + + if (token == QXmlStreamReader::StartElement) { + QStringRef tagName = reader.name(); + TagHandler handler = m_handlerMap.value(tagName.toString(), &QtXmlToSphinx::handleUnknownTag); + if (!m_handlers.isEmpty() && ( (m_handlers.top() == &QtXmlToSphinx::handleIgnoredTag) || + (m_handlers.top() == &QtXmlToSphinx::handleRawTag)) ) + handler = &QtXmlToSphinx::handleIgnoredTag; + + m_handlers.push(handler); + } + if (!m_handlers.isEmpty()) + (this->*(m_handlers.top()))(reader); + + if (token == QXmlStreamReader::EndElement) { + m_handlers.pop(); + m_lastTagName = reader.name().toString(); + } + } + m_output.flush(); + QString retval = popOutputBuffer(); + Q_ASSERT(m_buffers.isEmpty()); + return retval; +} + +QString QtXmlToSphinx::readFromLocation(QString& location, QString& identifier) +{ + QFile inputFile; + inputFile.setFileName(location); + if (!inputFile.open(QIODevice::ReadOnly)) { + ReportHandler::warning("Couldn't read code snippet file: "+inputFile.fileName()); + return QString(); + } + + QRegExp searchString("//!\\s*\\[" + identifier + "\\]"); + QRegExp codeSnippetCode("//!\\s*\\[[\\w\\d\\s]+\\]"); + QString code; + QString line; + bool identifierIsEmpty = identifier.isEmpty(); + bool getCode = false; + + while (!inputFile.atEnd()) { + line = inputFile.readLine(); + if (identifierIsEmpty) + code += line; + else if (getCode && !line.contains(searchString)) + code += line.replace(codeSnippetCode, ""); + else if (line.contains(searchString)) + if (getCode) + break; + else + getCode = true; + } + + if (code.isEmpty()) + ReportHandler::warning("Code snippet file found ("+location+"), but snippet "+ identifier +" not found."); + + return code; +} + +void QtXmlToSphinx::handleHeadingTag(QXmlStreamReader& reader) +{ + static QString heading; + static char type; + static char types[] = { '-', '^' }; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + int typeIdx = reader.attributes().value("level").toString().toInt(); + if (typeIdx >= sizeof(types)) + type = types[sizeof(types)-1]; + else + type = types[typeIdx]; + } else if (token == QXmlStreamReader::EndElement) { + m_output << createRepeatedChar(heading.length(), type) << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + heading = escape(reader.text()).trimmed(); + m_output << endl << endl << heading << endl; + } +} + +void QtXmlToSphinx::handleParaTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString result = popOutputBuffer().simplified(); + if (result.startsWith("**Warning:**")) + result.replace(0, 12, ".. warning:: "); + else if (result.startsWith("**Note:**")) + result.replace(0, 9, ".. note:: "); + + m_output << INDENT << result << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QString text = escape(reader.text()); + if (!m_output.string()->isEmpty()) { + QChar start = text[0]; + QChar end = m_output.string()->at(m_output.string()->length() - 1); + if ((end == '*' || end == '`') && start != ' ' && !start.isPunct()) + m_output << '\\'; + } + m_output << INDENT << text; + } +} + +void QtXmlToSphinx::handleItalicTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideItalic = !m_insideItalic; + m_output << '*'; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleBoldTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) { + m_insideBold = !m_insideBold; + m_output << "**"; + } else if (token == QXmlStreamReader::Characters) { + m_output << escape(reader.text()).trimmed(); + } +} + +void QtXmlToSphinx::handleArgumentTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement || token == QXmlStreamReader::EndElement) + m_output << "``"; + else if (token == QXmlStreamReader::Characters) + m_output << reader.text().toString().trimmed(); +} + +void QtXmlToSphinx::handleSeeAlsoTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + m_output << INDENT << ".. seealso:: "; + else if (token == QXmlStreamReader::EndElement) + m_output << endl; +} + +void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + QString location = reader.attributes().value("location").toString(); + QString identifier = reader.attributes().value("identifier").toString(); + location.prepend(m_generator->codeSnippetDir() + '/'); + QString code = readFromLocation(location, identifier); + if (!consecutiveSnippet) + m_output << INDENT << "::\n\n"; + + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} +void QtXmlToSphinx::handleDotsTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + bool consecutiveSnippet = m_lastTagName == "snippet" || m_lastTagName == "dots" || m_lastTagName == "codeline"; + if (consecutiveSnippet) { + m_output.flush(); + m_output.string()->chop(2); + } + Indentation indentation(INDENT); + pushOutputBuffer(); + m_output << INDENT; + int indent = reader.attributes().value("indent").toString().toInt(); + for (int i = 0; i < indent; ++i) + m_output << ' '; + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer() << "\n\n\n"; + } +} + +void QtXmlToSphinx::handleTableTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_currentTable.clear(); + m_tableHasHeader = false; + } else if (token == QXmlStreamReader::EndElement) { + // write the table on m_output + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleTermTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString().replace("::", "."); + } else if (token == QXmlStreamReader::EndElement) { + TableCell cell; + cell.data = popOutputBuffer().trimmed(); + m_currentTable << (TableRow() << cell); + } +} + + +void QtXmlToSphinx::handleItemTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + if (m_currentTable.isEmpty()) + m_currentTable << TableRow(); + TableRow& row = m_currentTable.last(); + TableCell cell; + cell.colSpan = reader.attributes().value("colspan").toString().toShort(); + cell.rowSpan = reader.attributes().value("rowspan").toString().toShort(); + row << cell; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::EndElement) { + QString data = popOutputBuffer().trimmed(); + if (!m_currentTable.isEmpty()) { + TableRow& row = m_currentTable.last(); + if (!row.isEmpty()) + row.last().data = data; + } + } +} + +void QtXmlToSphinx::handleRowTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_tableHasHeader = reader.name() == "header"; + m_currentTable << TableRow(); + } +} + +void QtXmlToSphinx::handleListTag(QXmlStreamReader& reader) +{ + // BUG We do not support a list inside a table cell + static QString listType; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + listType = reader.attributes().value("type").toString(); + if (listType == "enum") { + m_currentTable << (TableRow() << "Constant" << "Description"); + m_tableHasHeader = true; + } + INDENT.indent--; + } else if (token == QXmlStreamReader::EndElement) { + INDENT.indent++; + if (!m_currentTable.isEmpty()) { + if (listType == "bullet") { + m_output << endl; + foreach (TableCell cell, m_currentTable.first()) { + QStringList itemLines = cell.data.split('\n'); + m_output << INDENT << "* " << itemLines.first() << endl; + for (int i = 1, max = itemLines.count(); i < max; ++i) + m_output << INDENT << " " << itemLines[i] << endl; + } + m_output << endl; + } else if (listType == "enum") { + m_currentTable.enableHeader(m_tableHasHeader); + m_currentTable.normalize(); + m_output << m_currentTable; + } + } + m_currentTable.clear(); + } +} + +void QtXmlToSphinx::handleLinkTag(QXmlStreamReader& reader) +{ + static QString l_linktag; + static QString l_linkref; + static QString l_linktext; + static QString l_linktagending; + static QString l_type; + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + l_linktagending = "` "; + if (m_insideBold) { + l_linktag.prepend("**"); + l_linktagending.append("**"); + } else if (m_insideItalic) { + l_linktag.prepend('*'); + l_linktagending.append('*'); + } + l_type = reader.attributes().value("type").toString(); + + // TODO: create a flag PROPERTY-AS-FUNCTION to ask if the properties + // are recognized as such or not in the binding + if (l_type == "property") + l_type = "function"; + + if (l_type == "typedef") + l_type = "class"; + + QString linkSource; + if (l_type == "function" || l_type == "class") { + linkSource = "raw"; + } else if (l_type == "enum") { + linkSource = "enum"; + } else if (l_type == "page") { + linkSource = "page"; + } else { + linkSource = "href"; + } + + l_linkref = reader.attributes().value(linkSource).toString(); + l_linkref.replace("::", "."); + l_linkref.remove("()"); + + if (l_type == "function" && !m_context.isEmpty()) { + l_linktag = " :meth:`"; + QStringList rawlinklist = l_linkref.split("."); + if (rawlinklist.size() == 1 || rawlinklist[0] == m_context) + l_linkref.prepend("~" + m_context + '.'); + } else if (l_type == "function" && m_context.isEmpty()) { + l_linktag = " :func:`"; + } else if (l_type == "class") { + l_linktag = " :class:`"; + } else if (l_type == "enum") { + l_linktag = " :attr:`"; + } else if (l_type == "page" && l_linkref == m_generator->moduleName()) { + l_linktag = " :mod:`"; + } else { + l_linktag = " :ref:`"; + } + + } else if (token == QXmlStreamReader::Characters) { + QString linktext = reader.text().toString(); + linktext.replace("::", "."); + QString item = l_linkref.split(".").last(); + if (l_linkref == linktext + || (l_linkref + "()") == linktext + || item == linktext + || (item + "()") == linktext) + l_linktext.clear(); + else + l_linktext = linktext + QLatin1String("<"); + } else if (token == QXmlStreamReader::EndElement) { + if (!l_linktext.isEmpty()) + l_linktagending.prepend('>'); + m_output << l_linktag << l_linktext << escape(l_linkref) << l_linktagending; + } +} + +void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString href = reader.attributes().value("href").toString(); + QDir dir(m_generator->outputDirectory() + '/' + m_generator->packageName().replace(".", "/")); + QString imgPath = dir.relativeFilePath(m_generator->libSourceDir() + "/doc/src/") + '/' + href; + + if (reader.name() == "image") + m_output << INDENT << ".. image:: " << imgPath << endl << endl; + else + m_output << ".. image:: " << imgPath << ' '; + } +} + +void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << ".. raw:: " << format.toLower() << endl << endl; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + } +} + +void QtXmlToSphinx::handleCodeTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString format = reader.attributes().value("format").toString(); + m_output << INDENT << "::" << endl << endl; + INDENT.indent++; + } else if (token == QXmlStreamReader::Characters) { + QStringList lst(reader.text().toString().split("\n")); + foreach(QString row, lst) + m_output << INDENT << INDENT << row << endl; + } else if (token == QXmlStreamReader::EndElement) { + m_output << endl << endl; + INDENT.indent--; + } +} + +void QtXmlToSphinx::handleUnknownTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) + ReportHandler::warning("Unknow QtDoc tag: \"" + reader.name().toString() + "\"."); +} + +void QtXmlToSphinx::handleSuperScriptTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + m_output << " :sup:`"; + pushOutputBuffer(); + } else if (token == QXmlStreamReader::Characters) { + m_output << reader.text().toString(); + } else if (token == QXmlStreamReader::EndElement) { + m_output << popOutputBuffer(); + m_output << '`'; + } +} + +void QtXmlToSphinx::handleIgnoredTag(QXmlStreamReader&) +{ +} + +void QtXmlToSphinx::handleUselessTag(QXmlStreamReader&) +{ + // Tag "description" just marks the init of "Detailed description" title. + // Tag "definition" just marks enums. We have a different way to process them. +} + +void QtXmlToSphinx::handleAnchorTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::StartElement) { + QString anchor; + if (reader.attributes().hasAttribute("id")) + anchor = reader.attributes().value("id").toString(); + else if (reader.attributes().hasAttribute("name")) + anchor = reader.attributes().value("name").toString(); + if (!anchor.isEmpty() && m_opened_anchor != anchor) { + m_opened_anchor = anchor; + m_output << INDENT << ".. _" << m_context << "_" << anchor.toLower() << ":" << endl << endl; + } + } else if (token == QXmlStreamReader::EndElement) { + m_opened_anchor = ""; + } +} + +void QtXmlToSphinx::handleQuoteFileTag(QXmlStreamReader& reader) +{ + QXmlStreamReader::TokenType token = reader.tokenType(); + if (token == QXmlStreamReader::Characters) { + QString location = reader.text().toString(); + QString identifier = ""; + location.prepend(m_generator->libSourceDir() + '/'); + QString code = readFromLocation(location, identifier); + + m_output << INDENT << "::\n\n"; + Indentation indentation(INDENT); + if (code.isEmpty()) { + m_output << INDENT << "" << endl; + } else { + foreach (QString line, code.split("\n")) { + if (!QString(line).trimmed().isEmpty()) + m_output << INDENT << line; + + m_output << endl; + } + } + m_output << endl; + } +} + +void QtXmlToSphinx::Table::normalize() +{ + if (m_normalized || isEmpty()) + return; + + int row; + int col; + QtXmlToSphinx::Table& self = *this; + + // add col spans + for (row = 0; row < count(); ++row) { + for (col = 0; col < at(row).count(); ++col) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + if (cell.colSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.colSpan = -1; + for (int i = 0, max = cell.colSpan-1; i < max; ++i) { + self[row].insert(col+1, newCell); + } + cell.colSpan = 0; + col++; + } + } + } + + // row spans + const int numCols = first().count(); + for (col = 0; col < numCols; ++col) { + for (row = 0; row < count(); ++row) { + if (col < self[row].count()) { + QtXmlToSphinx::TableCell& cell = self[row][col]; + if (cell.rowSpan > 0) { + QtXmlToSphinx::TableCell newCell; + newCell.rowSpan = -1; + int max = std::min(cell.rowSpan - 1, count()); + cell.rowSpan = 0; + for (int i = 0; i < max; ++i) { + self[row+i+1].insert(col, newCell); + } + row++; + } + } + } + } + m_normalized = true; +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table) +{ + if (table.isEmpty()) + return s; + + if (!table.isNormalized()) { + ReportHandler::warning("Attempt to print an unnormalized table!"); + return s; + } + + // calc width and height of each column and row + QVector colWidths(table.first().count()); + QVector rowHeights(table.count()); + for (int i = 0, maxI = table.count(); i < maxI; ++i) { + const QtXmlToSphinx::TableRow& row = table[i]; + for (int j = 0, maxJ = row.count(); j < maxJ; ++j) { + QStringList rowLines = row[j].data.split('\n'); // cache this would be a good idea + foreach (QString str, rowLines) + colWidths[j] = std::max(colWidths[j], str.count()); + rowHeights[i] = std::max(rowHeights[i], row[j].data.count('\n') + 1); + } + } + + if (!*std::max_element(colWidths.begin(), colWidths.end())) + return s; // empty table (table with empty cells) + + // create a horizontal line to be used later. + QString horizontalLine("+"); + for (int i = 0, max = colWidths.count(); i < max; ++i) { + horizontalLine += createRepeatedChar(colWidths[i], '-'); + horizontalLine += '+'; + } + + // write table rows + for (int i = 0, maxI = table.count(); i < maxI; ++i) { // for each row + const QtXmlToSphinx::TableRow& row = table[i]; + + // print line + s << INDENT << '+'; + char c = (!i && table.hasHeader()) ? '=' : '-'; + for (int col = 0, max = colWidths.count(); col < max; ++col) { + char c; + if (row[col].rowSpan == -1) + c = ' '; + else if (i == 1 && table.hasHeader()) + c = '='; + else + c = '-'; + s << createRepeatedChar(colWidths[col], c) << '+'; + } + s << endl; + + + // Print the table cells + for (int rowLine = 0; rowLine < rowHeights[i]; ++rowLine) { // for each line in a row + for (int j = 0, maxJ = row.count(); j < maxJ; ++j) { // for each column + const QtXmlToSphinx::TableCell& cell = row[j]; + QStringList rowLines = cell.data.split('\n'); // FIXME: Cache this!!! + if (!j) // First column, so we need print the identation + s << INDENT; + + if (!j || !cell.colSpan) + s << '|'; + else + s << ' '; + s << qSetFieldWidth(colWidths[j]) << left; + s << (rowLine < rowLines.count() ? rowLines[rowLine] : ""); + s << qSetFieldWidth(0); + } + s << '|' << endl; + } + } + s << INDENT << horizontalLine << endl; + s << endl; + return s; +} + +static QString getClassName(const AbstractMetaClass *cppClass) { + return cppClass->name().replace("::", "."); +} + +static QString getFuncName(const AbstractMetaFunction *cppFunc) { + static bool hashInitialized = false; + static QHash operatorsHash; + if (!hashInitialized) { + operatorsHash.insert("operator+", "__add__"); + operatorsHash.insert("operator+=", "__iadd__"); + operatorsHash.insert("operator-", "__sub__"); + operatorsHash.insert("operator-=", "__isub__"); + operatorsHash.insert("operator*", "__mul__"); + operatorsHash.insert("operator*=", "__imul__"); + operatorsHash.insert("operator/", "__div__"); + operatorsHash.insert("operator/=", "__idiv__"); + operatorsHash.insert("operator%", "__mod__"); + operatorsHash.insert("operator%=", "__imod__"); + operatorsHash.insert("operator<<", "__lshift__"); + operatorsHash.insert("operator<<=", "__ilshift__"); + operatorsHash.insert("operator>>", "__rshift__"); + operatorsHash.insert("operator>>=", "__irshift__"); + operatorsHash.insert("operator&", "__and__"); + operatorsHash.insert("operator&=", "__iand__"); + operatorsHash.insert("operator|", "__or__"); + operatorsHash.insert("operator|=", "__ior__"); + operatorsHash.insert("operator^", "__xor__"); + operatorsHash.insert("operator^=", "__ixor__"); + operatorsHash.insert("operator==", "__eq__"); + operatorsHash.insert("operator!=", "__ne__"); + operatorsHash.insert("operator<", "__lt__"); + operatorsHash.insert("operator<=", "__le__"); + operatorsHash.insert("operator>", "__gt__"); + operatorsHash.insert("operator>=", "__ge__"); + hashInitialized = true; + } + + QHash::const_iterator it = operatorsHash.find(cppFunc->name()); + QString result = it != operatorsHash.end() ? it.value() : cppFunc->name(); + return result.replace("::", "."); +} + +QString DocGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const +{ + return QString("%1.rst").arg(getClassName(cppClass)); +} + +void DocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaClass) +{ + QString metaClassName; + + if (metaClass) + metaClassName = getClassName(metaClass); + + if (doc.format() == Documentation::Native) { + QtXmlToSphinx x(this, doc.value(), metaClassName); + s << x; + } else { + s << doc.value(); + } + + s << endl; +} + +void DocGenerator::writeFunctionBrief(QTextStream &s, + const AbstractMetaClass *cppClass, + const AbstractMetaFunction *cppFunction) +{ + s << INDENT << "def :meth:`" + << cppFunction->name() << "<"; + if (cppClass && cppClass->name() != cppFunction->name()) + s << getClassName(cppClass) << '.'; + + s << cppFunction->name() << ">`" + << " (" << parseArgDocStyle(cppClass, cppFunction) << "):"; +} + +void DocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass) +{ + QString doc; + QTextStream doc_s(&doc); + + ReportHandler::debugSparse("Generating Documentation for " + cppClass->fullName()); + s << ".. module:: " << packageName() << endl; + QString className = getClassName(cppClass); + s << ".. _" << className << ":" << endl << endl; + + s << className << endl; + s << createRepeatedChar(className.count(), '*') << endl << endl; + + s << ".. inheritance-diagram:: " << className << endl + << " :parts: 2" << endl << endl; // TODO: This would be a parameter in the future... + + //Function list + AbstractMetaFunctionList functionList = filterFunctions(cppClass); + qSort(functionList.begin(), functionList.end(), functionSort); + +#if 0 + if (functionList.size() > 0) + { + QtXmlToSphinx::Table functionTable; + QtXmlToSphinx::TableRow row; + + s << "Functions\n" + "---------\n\n"; + + + foreach (AbstractMetaFunction *func, functionList) { + if ((func->isConstructor() || func->isModifiedRemoved()) || + (func->declaringClass() != cppClass)) + continue; + + QString rowString; + QTextStream rowStream(&rowString); + + writeFunctionBrief(rowStream, cppClass, func); + row << rowString; + functionTable << row; + row.clear(); + } + functionTable.normalize(); + s << functionTable; + } + +#endif + + doc_s << "Detailed Description\n" + "--------------------\n\n"; + + writeInjectDocumentation(doc_s, DocModification::Prepend, cppClass, 0); + writeFormatedText(doc_s, cppClass->documentation(), cppClass); + + + if (!cppClass->isNamespace()) { + + writeConstructors(doc_s, cppClass); + writeEnums(doc_s, cppClass); + writeFields(doc_s, cppClass); + + foreach (AbstractMetaFunction *func, functionList) { + if ((func->isConstructor() || func->isModifiedRemoved()) || + (func->declaringClass() != cppClass)) + continue; + + if (func->isStatic()) + doc_s << ".. staticmethod:: "; + else + doc_s << ".. method:: "; + + writeFunction(doc_s, true, cppClass, func); + } + } + + writeInjectDocumentation(doc_s, DocModification::Append, cppClass, 0); + + writeFunctionList(s, doc, cppClass); + + s << doc; +} + +QString DocGenerator::parseFunctionDeclaration(const QString &doc, const AbstractMetaClass *cppClass) +{ + //.. method:: QObject.childEvent(arg__1) + //def :meth:`removeEventFilter` (arg__1): + + QString data = doc; + QString markup; + + if (data.startsWith(".. method::")) + markup = ".. method::"; + else if (data.startsWith(".. staticmethod::")) + markup = ".. staticmethod::"; + else + return QString(); + + data = data.mid(markup.size()); //remove .. method:: + data = data.mid(data.indexOf(".") + 1); //remove class name + + QString methName = data.mid(0, data.indexOf("(")); + QString methArgs = data.mid(data.indexOf("(")); + + data = QString("def :meth:`%1<%2.%3>` %4") + .arg(methName) + .arg(cppClass->name()) + .arg(methName) + .arg(methArgs); + + return data; +} + + +void DocGenerator::writeFunctionList(QTextStream &s, const QString &content, const AbstractMetaClass *cppClass) +{ + QStringList functionList; + QStringList staticFunctionList; + + QStringList lst = content.split("\n"); + foreach(QString row, lst) { + QString data = row.trimmed(); + if (data.startsWith(".. method::")) { + functionList << parseFunctionDeclaration(data, cppClass); + } + else if (data.startsWith(".. staticmethod::")) { + staticFunctionList << parseFunctionDeclaration(data, cppClass); + } + } + + if ((functionList.size() > 0) || (staticFunctionList.size() > 0)) + { + QtXmlToSphinx::Table functionTable; + QtXmlToSphinx::TableRow row; + + s << "Synopsis" << endl + << "--------" << endl << endl; + + if (functionList.size() > 0) { + s << "Functions" << endl + << "^^^^^^^^^" << endl << endl; + + qSort(functionList); + foreach (QString func, functionList) { + row << func; + functionTable << row; + row.clear(); + } + + functionTable.normalize(); + s << functionTable << endl; + functionTable.clear(); + } + + if (staticFunctionList.size() > 0) { + s << "Static functions" << endl + << "^^^^^^^^^^^^^^^^" << endl; + + qSort(staticFunctionList); + foreach (QString func, staticFunctionList) { + row << func; + functionTable << row; + row.clear(); + } + + functionTable.normalize(); + s << functionTable << endl; + } + } +} + +void DocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaEnum *en, cppClass->enums()) { + s << section_title << getClassName(cppClass) << "." << en->name() << endl << endl; + writeFormatedText(s, en->documentation(), cppClass); + } +} + +void DocGenerator::writeFields(QTextStream &s, const AbstractMetaClass *cppClass) +{ + static const QString section_title(".. attribute:: "); + + foreach (AbstractMetaField *field, cppClass->fields()) { + s << section_title << getClassName(cppClass) << "." << field->name() << endl << endl; + //TODO: request for member ‘documentation’ is ambiguous + writeFormatedText(s, field->AbstractMetaAttributes::documentation(), cppClass); + } +} + +void DocGenerator::writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass) +{ + static const QString sectionTitle = ".. class:: "; + static const QString sectionTitleSpace = QString(sectionTitle.size(), ' '); + + AbstractMetaFunctionList lst = cppClass->queryFunctions(AbstractMetaClass::Constructors | AbstractMetaClass::Visible); + + bool first = true; + QHash arg_map; + + foreach(AbstractMetaFunction *func, lst) { + if (func->isModifiedRemoved()) + continue; + + if (first) { + first = false; + s << sectionTitle; + } else { + s << sectionTitleSpace; + } + writeFunction(s, false, cppClass, func); + foreach(AbstractMetaArgument *arg, func->arguments()) + { + if (!arg_map.contains(arg->argumentName())) { + arg_map.insert(arg->argumentName(), arg); + } + } + } + + s << endl; + + foreach (AbstractMetaArgument *arg, arg_map.values()) { + Indentation indentation(INDENT); + writeParamerteType(s, cppClass, arg); + } + + s << endl; + + foreach (AbstractMetaFunction *func, lst) { + writeFormatedText(s, func->documentation(), cppClass); + } +} + +QString DocGenerator::parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func) +{ + QString ret; + bool optional = false; + + foreach (AbstractMetaArgument *arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + if (arg->argumentIndex() > 0) + ret += ","; + + if (!arg->defaultValueExpression().isEmpty() && (!optional)) { + ret += "["; + optional = true; + } + + ret += arg->argumentName(); + + if (optional) + ret += "=" + arg->defaultValueExpression(); + } + + if (optional) + ret += "]"; + + return ret; +} + +void DocGenerator::writeDocSnips(QTextStream &s, + const CodeSnipList &codeSnips, + CodeSnip::Position position, + TypeSystem::Language language) +{ + Indentation indentation(INDENT); + QStringList invalidStrings; + const static QString startMarkup("[sphinx-begin]"); + const static QString endMarkup("[sphinx-end]"); + + invalidStrings << "*" << "//" << "/*" << "*/"; + + foreach (CodeSnip snip, codeSnips) { + if ((snip.position != position) || + !(snip.language & language)) + continue; + + QString code = snip.code(); + while (code.contains(startMarkup) && code.contains(endMarkup)) { + int startBlock = code.indexOf(startMarkup) + startMarkup.size(); + int endBlock = code.indexOf(endMarkup); + + if ((startBlock == -1) || (endBlock == -1)) + break; + + QString codeBlock = code.mid(startBlock, endBlock - startBlock); + QStringList rows = codeBlock.split("\n"); + int currenRow = 0; + int offset = 0; + + foreach(QString row, rows) { + foreach(QString invalidString, invalidStrings) { + row = row.remove(invalidString); + } + + if (row.trimmed().size() == 0) { + if (currenRow == 0) + continue; + else + s << endl; + } + + if (currenRow == 0) { + //find offset + for (int i=0, i_max = row.size(); i < i_max; i++) { + if (row[i] == ' ') + offset++; + else if (row[i] == '\n') + offset = 0; + else + break; + } + } + row = row.mid(offset); + s << row << endl; + currenRow++; + } + + code = code.mid(endBlock+endMarkup.size()); + } + } +} + +void DocGenerator::writeInjectDocumentation(QTextStream &s, + DocModification::Mode mode, + const AbstractMetaClass *cppClass, + const AbstractMetaFunction *func) +{ + Indentation indentation(INDENT); + + foreach (DocModification mod, cppClass->typeEntry()->docModifications()) { + if (mod.mode() == mode) { + bool modOk = func ? mod.signature() == func->minimalSignature() : mod.signature().isEmpty(); + + if (modOk) { + Documentation doc; + Documentation::Format fmt; + + if (mod.format == TypeSystem::NativeCode) + fmt = Documentation::Native; + else if (mod.format == TypeSystem::TargetLangCode) + fmt = Documentation::Target; + else + continue; + + doc.setValue(mod.code() , fmt); + s << INDENT; + writeFormatedText(s, doc, cppClass); + } + } + } + + s << endl; + + if (func) { + writeDocSnips(s, getCodeSnips(func), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } else { + writeDocSnips(s, cppClass->typeEntry()->codeSnips(), + (mode == DocModification::Prepend ? CodeSnip::Beginning : CodeSnip::End), + TypeSystem::TargetLangCode); + } +} + +void DocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +{ + if (!func->isConstructor()) + s << getClassName(cppClass) << '.'; + s << getFuncName(func) << "(" << parseArgDocStyle(cppClass, func) << ")"; +} + +QString DocGenerator::translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass) +{ + QString originalType = translateType(type, cppClass, Generator::ExcludeConst | Generator::ExcludeReference); + QString strType = originalType; + + //remove "*" + strType.remove("*"); + TypeEntry *te = TypeDatabase::instance()->findType(originalType.trimmed()); + if (te) { + return te->targetLangName(); + } else { + //remove <, > + strType.remove(">"); + strType.remove("<"); + + //replace :: + strType.replace("::", "."); + + //Translate ContainerType + if (strType.contains("QList") || strType.contains("QVector")) { + strType.replace("QList", "List of "); + strType.replace("QVector", "List of "); + } else if (strType.contains("QHash") || strType.contains("QMap")) { + strType.remove("QHash"); + strType.remove("QMap"); + QStringList types = strType.split(","); + strType = QString("Dictionary with keys of type %1 and values of type %2.") + .arg(types[0]).arg(types[1]); + } + return strType; + } +} + +void DocGenerator::writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg) +{ + s << INDENT << ":param " << arg->argumentName() << ": " + << translateToPythonType(arg->type(), cppClass) << endl; +} + +void DocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +{ + Indentation indentation(INDENT); + + s << endl; + foreach (AbstractMetaArgument *arg, func->arguments()) { + + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + + writeParamerteType(s, cppClass, arg); + } + + if (!func->isConstructor() && func->type()) { + s << INDENT << ":rtype: " << translateToPythonType(func->type(), cppClass) << endl; + } + s << endl; +} + +void DocGenerator::writeFunction(QTextStream &s, bool writeDoc, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +{ + writeFunctionSignature(s, cppClass, func); + s << endl; + + if (writeDoc) { + s << endl; + writeFunctionParametersType(s, cppClass, func); + s << endl; + writeInjectDocumentation(s, DocModification::Prepend, cppClass, func); + writeFormatedText(s, func->documentation(), cppClass); + writeInjectDocumentation(s, DocModification::Append, cppClass, func); + } +} + +void DocGenerator::finishGeneration() +{ + if (classes().isEmpty()) + return; + + QFile input(outputDirectory() + '/' + subDirectoryForPackage(packageName()) + "/index.rst"); + input.open(QIODevice::WriteOnly); + QTextStream s(&input); + + s << ".. module:: " << packageName() << endl << endl; + + QString title = packageName() + " contents"; + s << title << endl; + s << createRepeatedChar(title.length(), '*') << endl << endl; + s << ".. toctree::" << endl; + + /* Avoid showing "Detailed Description for *every* class in toc tree */ + Indentation indentation(INDENT); + s << INDENT << ":maxdepth: 1" << endl << endl; + + QStringList classList; + foreach (AbstractMetaClass *cls, classes()) { + if (!shouldGenerate(cls)) + continue; + classList << getClassName(cls); + } + classList.sort(); + + foreach (QString clazz, classList) + s << INDENT << clazz << endl; + + s << endl << endl; + + s << "Detailed Description" << endl; + s << "--------------------" << endl << endl; + + if (m_moduleDoc.format() == Documentation::Native) { + QtXmlToSphinx x(this, m_moduleDoc.value(), moduleName()); + s << x; + } else { + s << m_moduleDoc.value(); + } +} + +bool DocGenerator::prepareGeneration(const QMap& args) +{ + ShibokenGenerator::prepareGeneration(args); + m_libSourceDir = args.value("library-source-dir"); + setOutputDirectory(args.value("documentation-out-dir")); + m_docDataDir = args.value("documentation-data-dir"); + m_codeSnippetDir = args.value("documentation-code-snippets-dir", m_libSourceDir); + + if (m_libSourceDir.isEmpty() || m_docDataDir.isEmpty()) { + ReportHandler::warning("Documentation data dir and/or Qt source dir not informed, " + "documentation will not be extracted from Qt sources."); + return false; + } else { + QtDocParser docParser; + docParser.setPackageName(packageName()); + docParser.setDocumentationDataDirectory(m_docDataDir); + docParser.setLibrarySourceDirectory(m_libSourceDir); + foreach(AbstractMetaClass* cppClass, classes()) { + docParser.fillDocumentation(cppClass); + } + m_moduleDoc = docParser.retrieveModuleDocumentation(); + return true; + } +} + + +QMap DocGenerator::options() const +{ + QMap options; + options.insert("library-source-dir", "Directory where library source code is located"); + options.insert("documentation-out-dir", "The directory where the generated documentation files will be written"); + options.insert("documentation-data-dir", "Directory with XML files generated by documentation tool (qdoc3 or Doxygen)"); + options.insert("documentation-code-snippets-dir", "Directory used to search code snippets used by the documentation"); + return options; +} + diff --git a/docgenerator.h b/docgenerator.h new file mode 100644 index 000000000..6298f41cb --- /dev/null +++ b/docgenerator.h @@ -0,0 +1,213 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef DOCGENERATOR_H +#define DOCGENERATOR_H + +#include "shibokengenerator.h" +#include + +class QXmlStreamReader; +class DocGenerator; + +class QtXmlToSphinx +{ +public: + struct TableCell + { + short rowSpan; + short colSpan; + QString data; + + TableCell(const QString& text = QString()) : rowSpan(0), colSpan(0), data(text) {} + TableCell(const char* text) : rowSpan(0), colSpan(0), data(text) {} + }; + + typedef QList TableRow; + class Table : public QList + { + public: + Table() : m_hasHeader(false), m_normalized(false) + { + } + + void enableHeader(bool enable) + { + m_hasHeader = enable; + } + + bool hasHeader() const + { + return m_hasHeader; + } + + void normalize(); + + bool isNormalized() const + { + return m_normalized; + } + + void clear() + { + m_normalized = false; + QList::clear(); + } + + private: + bool m_hasHeader; + bool m_normalized; + }; + + QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context = QString()); + + QString result() const + { + return m_result; + } + +private: + QString transform(const QString& doc); + + void handleHeadingTag(QXmlStreamReader& reader); + void handleParaTag(QXmlStreamReader& reader); + void handleItalicTag(QXmlStreamReader& reader); + void handleBoldTag(QXmlStreamReader& reader); + void handleArgumentTag(QXmlStreamReader& reader); + void handleSeeAlsoTag(QXmlStreamReader& reader); + void handleSnippetTag(QXmlStreamReader& reader); + void handleDotsTag(QXmlStreamReader& reader); + void handleLinkTag(QXmlStreamReader& reader); + void handleImageTag(QXmlStreamReader& reader); + void handleListTag(QXmlStreamReader& reader); + void handleTermTag(QXmlStreamReader& reader); + void handleSuperScriptTag(QXmlStreamReader& reader); + void handleQuoteFileTag(QXmlStreamReader& reader); + + // table tagsvoid QtXmlToSphinx::handleValueTag(QXmlStreamReader& reader) + + void handleTableTag(QXmlStreamReader& reader); + void handleRowTag(QXmlStreamReader& reader); + void handleItemTag(QXmlStreamReader& reader); + void handleRawTag(QXmlStreamReader& reader); + void handleCodeTag(QXmlStreamReader& reader); + + void handleIgnoredTag(QXmlStreamReader& reader); + void handleUnknownTag(QXmlStreamReader& reader); + void handleUselessTag(QXmlStreamReader& reader); + void handleAnchorTag(QXmlStreamReader& reader); + + typedef void (QtXmlToSphinx::*TagHandler)(QXmlStreamReader&); + QHash m_handlerMap; + QStack m_handlers; + QTextStream m_output; + QString m_result; + + QStack m_buffers; + + + Table m_currentTable; + bool m_tableHasHeader; + QString m_context; + DocGenerator* m_generator; + bool m_insideBold; + bool m_insideItalic; + QString m_lastTagName; + QString m_opened_anchor; + + QString readFromLocation(QString& location, QString& identifier); + void pushOutputBuffer(); + QString popOutputBuffer(); + void writeTable(Table& table); +}; + +inline QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx& xmlToSphinx) +{ + return s << xmlToSphinx.result(); +} + +QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table); + +/** +* The DocGenerator generates documentation from library being binded. +*/ +class DocGenerator : public ShibokenGenerator +{ +public: + virtual GeneratorType type() const + { + return DocumentationType; + } + + QString libSourceDir() const + { + return m_libSourceDir; + } + + virtual bool prepareGeneration(const QMap& args); + + const char* name() const + { + return "DocGenerator"; + } + + QMap options() const; + + QString codeSnippetDir() const + { + return m_codeSnippetDir; + } + +protected: + QString fileNameForClass(const AbstractMetaClass* cppClass) const; + void generateClass(QTextStream& s, const AbstractMetaClass* cppClass); + void finishGeneration(); +private: + void writeEnums(QTextStream& s, const AbstractMetaClass* cppClass); + + void writeFields(QTextStream &s, const AbstractMetaClass *cppClass); + void writeArguments(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + void writeFunctionBrief(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *cppFunction); + void writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunction(QTextStream& s, bool writeDoc, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func); + void writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func); + void writeFunctionList(QTextStream &se, const QString &content, const AbstractMetaClass *cppClass); + void writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg); + + void writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass); + void writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaclass = 0); + void writeInjectDocumentation(QTextStream &s, DocModification::Mode mode, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + void writeDocSnips(QTextStream &s, const CodeSnipList &codeSnips, CodeSnip::Position position, TypeSystem::Language language); + + + QString parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func); + QString parseFunctionDeclaration(const QString &data, const AbstractMetaClass *cppClass); + QString translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass); + + QString m_docDataDir; + QString m_libSourceDir; + QString m_codeSnippetDir; + QStringList m_functionList; + Documentation m_moduleDoc; +}; + +#endif // DOCGENERATOR_H diff --git a/headergenerator.cpp b/headergenerator.cpp new file mode 100644 index 000000000..fe4de388b --- /dev/null +++ b/headergenerator.cpp @@ -0,0 +1,412 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "headergenerator.h" +#include + +#include +#include +#include +#include +#include + +static Indentor INDENT; + +QString HeaderGenerator::fileNameForClass(const AbstractMetaClass* metaClass) const +{ + return metaClass->qualifiedCppName().toLower() + QLatin1String("_wrapper.h"); +} + +void HeaderGenerator::writeCopyCtor(QTextStream& s, const AbstractMetaClass* metaClass) const +{ + s << INDENT << wrapperName(metaClass) << "(const " << metaClass->qualifiedCppName() << "& self)"; + s << " : " << metaClass->qualifiedCppName() << "(self)" << endl; + s << INDENT << "{" << endl; + s << INDENT << "}" << endl << endl; +} + +void HeaderGenerator::generateClass(QTextStream& s, const AbstractMetaClass* metaClass) +{ + ReportHandler::debugSparse("Generating header for " + metaClass->fullName()); + Indentation indent(INDENT); + + // write license comment + s << licenseComment(); + + QString wrapperName = HeaderGenerator::wrapperName(metaClass); + + // Header + s << "#ifndef " << wrapperName.toUpper() << "_H" << endl; + s << "#define " << wrapperName.toUpper() << "_H" << endl<< endl; + + if (!metaClass->isNamespace()) { + s << "// The mother of all C++ binding hacks!" << endl; + s << "#define protected public" << endl << endl; + } + + s << "#include " << endl << endl; + + //Includes + if (metaClass->typeEntry()->include().isValid()) + s << metaClass->typeEntry()->include().toString() << endl << endl; + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), + CodeSnip::Declaration, TypeSystem::NativeCode); + + if (!metaClass->isNamespace()) { + bool createWrapper = canCreateWrapperFor(metaClass); + + /* + * BOTOWTI (Beast of The Old World to be Investigated) + // detect the held type + QString held_type = metaClass->typeEntry()->heldTypeValue(); + if (held_type.isEmpty() && createWrapper) { + held_type = "qptr"; + } + + if (!held_type.isEmpty()) { + s << "// held type forward decalration" << endl; + s << "template class " << held_type << ';' << endl; + } + */ + + // Class + s << "class SHIBOKEN_LOCAL " << wrapperName; + if (createWrapper) + s << " : public " << metaClass->qualifiedCppName(); + + s << endl << '{' << endl << "public:" << endl; + + if (metaClass->hasCloneOperator()) + writeCopyCtor(s, metaClass); + + foreach (AbstractMetaFunction *func, filterFunctions(metaClass)) + writeFunction(s, func); + + if (createWrapper) { + //destructor + s << INDENT << "~" << wrapperName << "();" << endl; + + if (metaClass->isQObject() && (metaClass->name() != "QObject")) + s << INDENT << "using QObject::parent;" << endl; + } + + writeCodeSnips(s, metaClass->typeEntry()->codeSnips(), + CodeSnip::PrototypeInitialization, TypeSystem::NativeCode); + + s << "};" << endl << endl; + } + + s << "#endif // " << wrapperName.toUpper() << "_H" << endl << endl; +} + +void HeaderGenerator::writeFunction(QTextStream& s, const AbstractMetaFunction* func) const +{ + // pure virtual functions need a default implementation + if (func->isPrivate() || (func->isModifiedRemoved() && !func->isAbstract())) + return; + + // do not write copy ctors here. + if (func->isCopyConstructor()) + return; + + if (func->isConstructor() || func->isAbstract() || func->isVirtual()) { + s << INDENT << functionSignature(func) << ';' << endl; + + // TODO: when modified an abstract method ceases to be virtual but stays abstract + //if (func->isModifiedRemoved() && func->isAbstract()) { + //} + + // TODO: APIExtractor: strange that something that is abstract couldn't be considered virtual too. + if (func->isVirtual() && !func->isAbstract() && !func->isConstructor() && + !func->ownerClass()->hasPrivateDestructor() && + func->implementingClass() == func->ownerClass()) { + writeVirtualDispatcher(s, func); + } + } +} + +void HeaderGenerator::writeVirtualDispatcher(QTextStream& s, const AbstractMetaFunction* func) const +{ + QString returnKeyword = func->type() ? QLatin1String("return ") : QString(); + s << INDENT << "static " << signatureForDefaultVirtualMethod(func, "", "_dispatcher") << " {" << endl; + { + Indentation indentation(INDENT); + s << INDENT << returnKeyword; + if (func->isModifiedRemoved() && func->isAbstract()) { + if (func->type() + && (func->type()->isObject() + || func->type()->isQObject() + || func->type()->name() == "void")) + s << "0"; + else + s << functionReturnType(func) << "()"; + } else { + s << "self." << func->implementingClass()->qualifiedCppName() << "::"; + writeFunctionCall(s, func); + } + s << ';' << endl; + } + s << INDENT << '}' << endl; +} + +void HeaderGenerator::writeTypeCheckMacro(QTextStream& s, const TypeEntry* type) +{ + QString pyTypeName = cpythonTypeName(type); + QString checkFunction = cpythonCheckFunction(type); + s << "PyAPI_DATA(PyTypeObject) " << pyTypeName << ';' << endl; + s << "#define " << checkFunction << "(op) PyObject_TypeCheck(op, &"; + s << pyTypeName << ')' << endl; + s << "#define " << checkFunction << "Exact(op) ((op)->ob_type == &"; + s << pyTypeName << ')' << endl; +} + +void HeaderGenerator::writeTypeConverter(QTextStream& s, const TypeEntry* type) +{ + QString pyTypeName = cpythonTypeName(type); + QString checkFunction = cpythonCheckFunction(type); + QString cppName = type->name(); + if (type->isObject()) + cppName.append('*'); + + s << "template<>" << endl; + s << "struct Converter< " << cppName << " >" << endl << '{' << endl; + + s << INDENT << "static PyObject* toPython(ValueHolder< "; + s << cppName << " > cppobj)" << endl << INDENT << '{' << endl; + { + Indentation indent(INDENT); + s << INDENT << "PyObject* pyobj;" << endl; + + if (!type->isEnum()) { + s << INDENT << "ValueHolder holder((void*) "; + if (type->isValue()) + s << "new " << cppName << "(cppobj.value)"; + else + s << "cppobj.value"; + s << ");" << endl; + } + + s << INDENT << "pyobj = "; + + if (type->isEnum()) { + s << "Shiboken::PyEnumObject_New(&" << pyTypeName << ',' << endl; + s << INDENT << INDENT << "\"ReturnedValue\", (long) cppobj.value);" << endl; + } else { + QString newWrapper = QString("Shiboken::PyBaseWrapper_New(&") + + pyTypeName + ", &" + pyTypeName + + ", holder.value);"; + if (type->isValue()) { + s << newWrapper << endl; + } else { + s << "Shiboken::Converter::toPython(holder);" << endl; + s << INDENT << "if (!pyobj)" << endl; + { + Indentation indent(INDENT); + s << INDENT << "pyobj = " << newWrapper << endl; + } + } + } + + s << INDENT << "return pyobj;" << endl; + } + s << INDENT << '}' << endl; + + s << INDENT << "static inline " << cppName << " toCpp(PyObject* pyobj)" << endl; + s << INDENT << '{' << endl; + { + Indentation indent(INDENT); + + if (type->isValue()) { + AbstractMetaFunctionList implicitConverters; + if (type->isValue()) { + const AbstractMetaClass* metaClass = classes().findClass(type->qualifiedCppName()); + if (metaClass) + implicitConverters = metaClass->implicitConversions(); + } + bool firstImplicitIf = true; + foreach (const AbstractMetaFunction* ctor, implicitConverters) { + const AbstractMetaType* argType = ctor->arguments().first()->type(); + s << INDENT; + if (firstImplicitIf) + firstImplicitIf = false; + else + s << "else "; + s << "if (" << cpythonCheckFunction(argType) << "(pyobj))" << endl; + { + Indentation indent(INDENT); + s << INDENT << "return " << cppName; + s << "(Converter< " << argType->cppSignature() << " >::toCpp(pyobj));" << endl; + } + } + } + + s << INDENT << "return "; + if (type->isEnum()) { + s << '(' << type->qualifiedCppName() << ") ((Shiboken::PyEnumObject*)pyobj)->ob_ival"; + } else { + if (type->isValue()) + s << '*'; + s << "((" << cppName; + if (type->isValue()) + s << '*'; + s << ") ((Shiboken::PyBaseWrapper*)pyobj)->cptr)"; + } + s << ';' << endl; + } + s << INDENT << '}' << endl << "};" << endl; +} + +void HeaderGenerator::finishGeneration() +{ + // Generate the main header for this module. + // This header should be included by binding modules + // extendind on top of this one. + QString classIncludes; + QTextStream s_cin(&classIncludes); + QSet enumIncludes; + QString pythonTypeStuff; + QTextStream s_pts(&pythonTypeStuff); + QString converters; + QTextStream s_c(&converters); + + Indentation indent(INDENT); + + s_pts << endl << "// Global enums" << endl; + foreach (const AbstractMetaEnum* cppEnum, globalEnums()) { + QString incFile = cppEnum->includeFile().split(QDir::separator()).takeLast(); + if (!incFile.isEmpty() && !classIncludes.contains(QString("<%1>").arg(incFile))) + enumIncludes << incFile; + writeTypeCheckMacro(s_pts, cppEnum->typeEntry()); + s_pts << endl; + writeTypeConverter(s_c, cppEnum->typeEntry()); + s_c << endl; + } + + foreach (AbstractMetaClass* metaClass, classes()) { + const TypeEntry* classType = metaClass->typeEntry(); + if (!shouldGenerate(metaClass) || metaClass->enclosingClass() || + !(classType->isObject() || classType->isValue() || classType->isNamespace())) + continue; + + if (m_packageName.isEmpty()) + m_packageName = metaClass->package(); + + //Includes + if (metaClass->typeEntry()->include().isValid()) + s_cin << metaClass->typeEntry()->include().toString() << endl; + + foreach (AbstractMetaClass* innerClass, metaClass->innerClasses()) { + if (shouldGenerate(innerClass)) + s_cin << innerClass->typeEntry()->include().toString() << endl; + } + + foreach (const AbstractMetaEnum* cppEnum, metaClass->enums()) { + writeTypeCheckMacro(s_pts, cppEnum->typeEntry()); + s_pts << endl; + writeTypeConverter(s_c, cppEnum->typeEntry()); + s_c << endl; + } + + if (!metaClass->isNamespace()) { + s_pts << "PyAPI_FUNC(PyObject*) " << cpythonBaseName(metaClass->typeEntry()); + s_pts << "_New(PyTypeObject* type, PyObject* args, PyObject* kwds);" << endl; + writeTypeCheckMacro(s_pts, classType); + s_pts << "#define Py" << metaClass->name() << "_cptr(pyobj) (("; + s_pts << metaClass->name() << "*)PyBaseWrapper_cptr(pyobj))" << endl << endl; + writeTypeConverter(s_c, classType); + s_c << endl; + } + } + + QString moduleHeaderFileName(outputDirectory() + QDir::separator() + + subDirectoryForPackage(m_packageName)); + moduleHeaderFileName += QDir::separator() + moduleName().toLower() + "_python.h"; + + QString includeShield = moduleName().toUpper() + "_PYTHON_H"; + + QFile file(moduleHeaderFileName); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + + // write license comment + s << licenseComment() << endl << endl; + + s << "#ifndef " << includeShield << endl; + s << "#define " << includeShield << endl<< endl; + + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl; + s << "#include " << endl << endl; + + s << "// Class Includes" << endl; + s << classIncludes << endl; + + if (!enumIncludes.isEmpty()) { + s << "// Enum Includes" << endl; + foreach (const QString& include, enumIncludes) + s << "#include <" << include << ">" << endl; + s << endl; + } + + if (!containerTypes().isEmpty()) { + s << "// Conversion Includes" << endl; + foreach (const ContainerTypeEntry* ctype, containerTypes()) { + if (ctype->include().isValid()) + s << ctype->include().toString() << endl; + } + s << endl; + } + + s << "extern \"C\"" << endl << '{' << endl << endl; + s << pythonTypeStuff << endl; + s << "} // extern \"C\"" << endl << endl; + + s << "namespace Shiboken" << endl << '{' << endl << endl; + + s << "// User defined converters --------------------------------------------" << endl; + + foreach (const PrimitiveTypeEntry* ptype, primitiveTypes()) { + if (!ptype->codeSnips().isEmpty()) { + foreach (CodeSnip snip, ptype->codeSnips()) + s << snip.code(); + } + } + + foreach (const ContainerTypeEntry* ctype, containerTypes()) { + if (!ctype->codeSnips().isEmpty()) { + foreach (CodeSnip snip, ctype->codeSnips()) + s << snip.code(); + } + } + + s << "// Generated converters -----------------------------------------------" << endl << endl; + + s << converters << endl; + + s << "} // namespace Shiboken" << endl << endl; + + s << "#endif // " << includeShield << endl << endl; + } +} diff --git a/headergenerator.h b/headergenerator.h new file mode 100644 index 000000000..32c17bef6 --- /dev/null +++ b/headergenerator.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef HEADERGENERATOR_H +#define HEADERGENERATOR_H + +#include "shibokengenerator.h" + +/** + * The HeaderGenerator generate the declarations of C++ bindings classes. + */ +class HeaderGenerator : public ShibokenGenerator +{ +protected: + QString fileNameForClass(const AbstractMetaClass* metaClass) const; + void generateClass(QTextStream& s, const AbstractMetaClass* metaClass); + void finishGeneration(); +private: + void writeCopyCtor(QTextStream &s, const AbstractMetaClass* metaClass) const; + void writeFunction(QTextStream& s, const AbstractMetaFunction* func) const; + void writePureVirtualEmptyImpl(QTextStream& , const AbstractMetaFunction* func) const; + void writeDefaultImplementation(QTextStream& s, const AbstractMetaFunction* func) const; + void writeVirtualDispatcher(QTextStream &s, const AbstractMetaFunction *func) const; + void writeTypeCheckMacro(QTextStream& s, const TypeEntry* type); + void writeTypeConverter(QTextStream& s, const TypeEntry* type); +}; + +#endif // HEADERGENERATOR_H + diff --git a/libshiboken/CMakeLists.txt b/libshiboken/CMakeLists.txt new file mode 100644 index 000000000..8471fad8d --- /dev/null +++ b/libshiboken/CMakeLists.txt @@ -0,0 +1,37 @@ +project(libshiboken) + +find_package(PythonLibs REQUIRED) + +set(libshiboken_VERSION 0.1) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/FindShiboken.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/FindShiboken.cmake @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/shiboken.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/shiboken.pc @ONLY) + +set(libshiboken_SRC +basewrapper.cpp +containers.cpp +helper.cpp +pyenum.cpp +bindingmanager.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${PYTHON_INCLUDE_PATH}) +add_library(libshiboken SHARED ${libshiboken_SRC}) +set_property(TARGET libshiboken PROPERTY PREFIX "") +target_link_libraries(libshiboken + ${PYTHON_LIBRARIES} + -lutil) + +install(DIRECTORY . DESTINATION include/shiboken + FILES_MATCHING PATTERN "*.h" + PATTERN ".git" EXCLUDE + ) +install(TARGETS libshiboken DESTINATION lib) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FindShiboken.cmake + DESTINATION share/cmake-2.6/Modules) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/shiboken.pc + DESTINATION lib/pkgconfig) + diff --git a/libshiboken/FindShiboken.cmake.in b/libshiboken/FindShiboken.cmake.in new file mode 100644 index 000000000..e63310dc8 --- /dev/null +++ b/libshiboken/FindShiboken.cmake.in @@ -0,0 +1,13 @@ +# - try to find Shiboken +# SHIBOKEN_INCLUDE_DIR - Directories to include to use Shiboken +# SHIBOKEN_LIBRARIES - Files to link against to use Shiboken +# SHIBOKEN_FOUND - Shiboken was found + +find_path(SHIBOKEN_INCLUDE_DIR shiboken.h @CMAKE_INSTALL_PREFIX@/include/shiboken) +find_library(SHIBOKEN_LIBRARY shiboken @CMAKE_INSTALL_PREFIX@/lib) + +set(SHIBOKEN_FOUND "NO") +if(SHIBOKEN_LIBRARY AND SHIBOKEN_INCLUDE_DIR) + set(SHIBOKEN_FOUND "YES") +endif(SHIBOKEN_LIBRARY AND SHIBOKEN_INCLUDE_DIR) + diff --git a/libshiboken/basewrapper.cpp b/libshiboken/basewrapper.cpp new file mode 100644 index 000000000..d2c5101c5 --- /dev/null +++ b/libshiboken/basewrapper.cpp @@ -0,0 +1,56 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "basewrapper.h" +#include "bindingmanager.h" + +namespace Shiboken +{ + +PyObject* +PyBaseWrapper_New(PyTypeObject* instanceType, PyTypeObject* baseWrapperType, void* cptr, uint hasOwnership) +{ + if (!cptr) + return 0; + + PyObject *self = instanceType->tp_alloc(instanceType, 0); + ((Shiboken::PyBaseWrapper*)self)->baseWrapperType = baseWrapperType; + ((Shiboken::PyBaseWrapper*)self)->cptr = cptr; + ((Shiboken::PyBaseWrapper*)self)->hasOwnership = hasOwnership; + ((Shiboken::PyBaseWrapper*)self)->validCppObject = 1; + BindingManager::instance().assignWrapper(self, cptr); + return self; +} + +} // namespace Shiboken diff --git a/libshiboken/basewrapper.h b/libshiboken/basewrapper.h new file mode 100644 index 000000000..201851cc6 --- /dev/null +++ b/libshiboken/basewrapper.h @@ -0,0 +1,92 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef BASEWRAPPER_H +#define BASEWRAPPER_H + +#include + +namespace Shiboken +{ + +extern "C" +{ + +struct PyBaseWrapper +{ + PyObject_HEAD + PyTypeObject* baseWrapperType; + void* cptr; + uint hasOwnership : 1; + uint validCppObject : 1; +}; + +} // extern "C" + +#define PyBaseWrapper_Check(op) PyObject_TypeCheck(op, &PyBaseWrapper_Type) +#define PyBaseWrapper_CheckExact(op) ((op)->ob_type == &PyBaseWrapper_Type) + +#define PyBaseWrapper_cptr(pyobj) (((Shiboken::PyBaseWrapper*)pyobj)->cptr) +#define PyBaseWrapper_setCptr(pyobj,c) (((Shiboken::PyBaseWrapper*)pyobj)->cptr = c) +#define PyBaseWrapper_hasOwnership(pyobj) (((Shiboken::PyBaseWrapper*)pyobj)->hasOwnership) +#define PyBaseWrapper_setOwnership(pyobj,o) (((Shiboken::PyBaseWrapper*)pyobj)->hasOwnership = o) +#define PyBaseWrapper_validCppObject(pyobj) (((Shiboken::PyBaseWrapper*)pyobj)->validCppObject) +#define PyBaseWrapper_setValidCppObject(pyobj,v) (((Shiboken::PyBaseWrapper*)pyobj)->validCppObject = v) + +PyAPI_FUNC(PyObject*) +PyBaseWrapper_New(PyTypeObject *instanceType, PyTypeObject *baseWrapperType, + void *cptr, uint hasOwnership = 1); + +inline bool +cppObjectIsValid(PyBaseWrapper* self) +{ + if (self->validCppObject) + return true; + PyErr_SetString(PyExc_RuntimeError, "internal C++ object already deleted."); + return false; +} + +template +PyAPI_FUNC(void) +PyBaseWrapper_Dealloc(PyObject* self) +{ + if (PyBaseWrapper_hasOwnership(self)) { + delete ((T*)PyBaseWrapper_cptr(self)); + } + Py_TYPE(((PyBaseWrapper*)self))->tp_free((PyObject*)self); +} + +} // namespace Shiboken + +#endif // BASEWRAPPER_H diff --git a/libshiboken/bindingmanager.cpp b/libshiboken/bindingmanager.cpp new file mode 100644 index 000000000..c81ad07b4 --- /dev/null +++ b/libshiboken/bindingmanager.cpp @@ -0,0 +1,107 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "bindingmanager.h" + +namespace Shiboken +{ + +BindingManager& BindingManager::instance() { + static BindingManager singleton; + return singleton; +} + +bool BindingManager::hasWrapper(void* cptr) +{ + return m_wrapperMapper.count(cptr); +} + +void BindingManager::assignWrapper(PyObject* wrapper, void* cptr) +{ + std::map::iterator iter = m_wrapperMapper.find(cptr); + if (iter == m_wrapperMapper.end()) + m_wrapperMapper.insert(std::pair(cptr, wrapper)); + else + iter->second = wrapper; +} + +void BindingManager::releaseWrapper(void *cptr) +{ + std::map::iterator iter = m_wrapperMapper.find(cptr); + if (iter != m_wrapperMapper.end()) + m_wrapperMapper.erase(iter); +} + +inline void BindingManager::releaseWrapper(PyObject* wrapper) +{ + releaseWrapper(PyBaseWrapper_cptr(wrapper)); +} + +PyObject* BindingManager::retrieveWrapper(void* cptr) +{ + std::map::iterator iter = m_wrapperMapper.find(cptr); + if (iter == m_wrapperMapper.end()) + return 0; + return iter->second; +} + +PyObject* BindingManager::getOverride(void* cptr, const char* methodName) +{ + PyObject* wrapper = retrieveWrapper(cptr); + + fprintf(stderr, "[%s:%d] method: %s, wrapper: %s\n", __FUNCTION__, __LINE__, methodName, wrapper->ob_type->tp_name); + + if (wrapper) { + PyTypeObject* baseWrapperType = ((Shiboken::PyBaseWrapper*)wrapper)->baseWrapperType; + fprintf(stderr, "[%s:%d] basewrapper: %s\n", __FUNCTION__, __LINE__, baseWrapperType->tp_name); + PyObject* method = PyObject_GetAttrString(wrapper, const_cast(methodName)); + if (method != 0) { + PyObject* defaultMethod = 0; + if (PyMethod_Check(method) && + ((PyMethodObject*) method)->im_self == wrapper && + baseWrapperType->tp_dict != 0) { + defaultMethod = PyDict_GetItemString(baseWrapperType->tp_dict, const_cast(methodName)); + } + + if (((PyMethodObject*)method)->im_func != defaultMethod) + return method; + + Py_DECREF(method); + } + } + + return 0; +} + +} // namespace Shiboken diff --git a/libshiboken/bindingmanager.h b/libshiboken/bindingmanager.h new file mode 100644 index 000000000..bd4472154 --- /dev/null +++ b/libshiboken/bindingmanager.h @@ -0,0 +1,66 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef BINDINGMANAGER_H +#define BINDINGMANAGER_H + +#include +#include +#include + +namespace Shiboken +{ + +class BindingManager +{ +public: + static BindingManager& instance(); + + bool hasWrapper(void *cptr); + void assignWrapper(PyObject* wrapper, void* cptr); + void releaseWrapper(void* cptr); + inline void releaseWrapper(PyObject* wrapper); + PyObject* retrieveWrapper(void* cptr); + PyObject* getOverride(void* cptr, const char* methodName); + +private: + BindingManager() {} + BindingManager(const BindingManager&); + + std::map m_wrapperMapper; +}; + +} // namespace Shiboken + +#endif // BINDINGMANAGER_H diff --git a/libshiboken/containers.cpp b/libshiboken/containers.cpp new file mode 100644 index 000000000..ce756871f --- /dev/null +++ b/libshiboken/containers.cpp @@ -0,0 +1,41 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "containers.h" + +namespace Shiboken +{ + + +} // namespace Shiboken diff --git a/libshiboken/containers.h b/libshiboken/containers.h new file mode 100644 index 000000000..85cadb0df --- /dev/null +++ b/libshiboken/containers.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef CONTAINERS_H +#define CONTAINERS_H + +#include + +namespace Shiboken +{ + + +} // namespace Shiboken + +#endif // CONTAINERS_H + diff --git a/libshiboken/conversions.h b/libshiboken/conversions.h new file mode 100644 index 000000000..cbd4ee550 --- /dev/null +++ b/libshiboken/conversions.h @@ -0,0 +1,191 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef CONVERSIONS_H +#define CONVERSIONS_H + +#include +#include +#include + +namespace Shiboken +{ + +template +struct ValueHolder +{ + explicit ValueHolder(T val) : value(val) {} + T value; +}; + +template +struct Converter +{ + static PyObject* toPython(ValueHolder cppobj) { + return 0; + } + static T toCpp(PyObject* pyobj) { + return T(); + } +}; + +// Object Types --------------------------------------------------------------- +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder cppobj) + { + PyObject* obj = BindingManager::instance().retrieveWrapper(cppobj.value); + Py_XINCREF(obj); + return obj; + } + static void* toCpp(PyObject* pyobj) + { + return ((Shiboken::PyBaseWrapper*) pyobj)->cptr; + } +}; + +// Primitive Types ------------------------------------------------------------ +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder holder) + { + return PyBool_FromLong(holder.value); + } + static bool toCpp(PyObject* pyobj) + { + return pyobj == Py_True; + } +}; + +template +struct Converter_PyInt +{ + static PyObject* toPython(ValueHolder holder) + { + return PyInt_FromLong((long) holder.value); + } + static PyIntEquiv toCpp(PyObject* pyobj) + { + if (PyFloat_Check(pyobj)) + return (PyIntEquiv) PyFloat_AS_DOUBLE(pyobj); + return (PyIntEquiv) PyInt_AS_LONG(pyobj); + } +}; + +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; +template <> struct Converter : Converter_PyInt {}; + +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder holder) + { + return PyLong_FromUnsignedLong(holder.value); + } + static unsigned long toCpp(PyObject* pyobj) + { + return (unsigned long) PyLong_AsUnsignedLong(pyobj); + } +}; + +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder holder) + { + return PyLong_FromLongLong(holder.value); + } + static PY_LONG_LONG toCpp(PyObject* pyobj) + { + return (PY_LONG_LONG) PyLong_AsLongLong(pyobj); + } +}; + +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder holder) + { + return PyLong_FromUnsignedLongLong(holder.value); + } + static unsigned PY_LONG_LONG toCpp(PyObject* pyobj) + { + return (unsigned PY_LONG_LONG) PyLong_AsUnsignedLongLong(pyobj); + } +}; + +template +struct Converter_PyFloat +{ + static PyObject* toPython(ValueHolder holder) + { + return PyFloat_FromDouble((double) holder.value); + } + static PyFloatEquiv toCpp(PyObject* pyobj) + { + if (PyInt_Check(pyobj)) + return (PyFloatEquiv) PyInt_AS_LONG(pyobj); + return (PyFloatEquiv) PyFloat_AS_DOUBLE(pyobj); + } +}; + +template <> struct Converter : Converter_PyFloat {}; +template <> struct Converter : Converter_PyFloat {}; + +// C Sting Types -------------------------------------------------------------- + +template <> +struct Converter +{ + static PyObject* toPython(ValueHolder holder) + { + return PyString_FromString(holder.value); + } + static const char* toCpp(PyObject* pyobj) + { + return PyString_AsString(pyobj); + } +}; + +} // namespace Shiboken + +#endif // CONVERSIONS_H + diff --git a/libshiboken/helper.cpp b/libshiboken/helper.cpp new file mode 100644 index 000000000..eb213ed3d --- /dev/null +++ b/libshiboken/helper.cpp @@ -0,0 +1,67 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "helper.h" + +namespace Shiboken +{ + +int +PySequence_to_argc_argv(_object* argList, char** argv[]) +{ + if (!PySequence_Check(argList)) + return -1; + + int argc = (int) PySequence_Size(argList); + (*argv) = new char*[argc]; + for (int i = 0; i < argc; ++i) { + PyObject* item = PySequence_GetItem(argList, i); + if (!PyString_Check(item)) { + argc = -1; + for (int j = 0; j < i; ++j) + delete (*argv)[j]; + Py_DECREF(item); + return -1; + } + char *origArg = PyString_AS_STRING(item); + int size = strlen(origArg); + (*argv)[i] = new char[size+1]; + (*argv)[i] = strcpy((*argv)[i], origArg); + Py_DECREF(item); + } + + return argc; +} + +} // namespace Shiboken diff --git a/libshiboken/helper.h b/libshiboken/helper.h new file mode 100644 index 000000000..588f0c3f0 --- /dev/null +++ b/libshiboken/helper.h @@ -0,0 +1,72 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef HELPER_H +#define HELPER_H + +#include + +namespace Shiboken +{ + +// Generic helper definitions for shared library support +#if defined _WIN32 || defined __CYGWIN__ +#define SHIBOKEN_HELPER_DLL_IMPORT __declspec(dllimport) +#define SHIBOKEN_HELPER_DLL_EXPORT __declspec(dllexport) +#define SHIBOKEN_HELPER_DLL_LOCAL +#else +#if __GNUC__ >= 4 +#define SHIBOKEN_HELPER_DLL_IMPORT __attribute__ ((visibility("default"))) +#define SHIBOKEN_HELPER_DLL_EXPORT __attribute__ ((visibility("default"))) +#define SHIBOKEN_HELPER_DLL_LOCAL __attribute__ ((visibility("internal"))) +#else +#define SHIBOKEN_HELPER_DLL_IMPORT +#define SHIBOKEN_HELPER_DLL_EXPORT +#define SHIBOKEN_HELPER_DLL_LOCAL +#endif +#endif + +// Now we use the generic helper definitions above to define SHIBOKEN_API and SHIBOKEN_LOCAL. +// SHIBOKEN_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) +// SHIBOKEN_LOCAL is used for non-api symbols. + +#define SHIBOKEN_API SHIBOKEN_HELPER_DLL_EXPORT +#define SHIBOKEN_LOCAL SHIBOKEN_HELPER_DLL_LOCAL + +int PySequence_to_argc_argv(PyObject* argList, char** argv[]); + +} // namespace Shiboken + +#endif // HELPER_H + diff --git a/libshiboken/pyenum.cpp b/libshiboken/pyenum.cpp new file mode 100644 index 000000000..d60d03443 --- /dev/null +++ b/libshiboken/pyenum.cpp @@ -0,0 +1,96 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "pyenum.h" + +namespace Shiboken +{ + +PyObject* +PyEnumObject_New(PyTypeObject *type, PyObject* item_name, long item_value) +{ + PyEnumObject* enum_obj = (PyEnumObject*) type->tp_alloc(type, 0); + enum_obj->ob_name = item_name; + enum_obj->ob_ival = item_value; + return (PyObject*) enum_obj; +} + +PyObject* +PyEnumObject_New(PyTypeObject *type, const char* item_name, long item_value) +{ + PyObject* py_item_name = PyString_FromString(item_name); + PyObject* enum_obj = PyEnumObject_New(type, py_item_name, item_value); + if (!enum_obj) { + Py_DECREF(py_item_name); + return 0; + } + PyObject* values = PyDict_GetItemString(type->tp_dict, const_cast("values")); + if (!values) { + values = PyDict_New(); + PyDict_SetItemString(type->tp_dict, const_cast("values"), values); + } + PyDict_SetItemString(values, item_name, enum_obj); + return enum_obj; +} + +extern "C" +{ + +PyObject* +PyEnumObject_NonExtensibleNew(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyErr_SetString(PyExc_TypeError, "this enum is not extensible"); + return 0; +} + + +PyObject* +PyEnumObject_repr(PyObject* self) +{ + return PyString_FromFormat("", + self->ob_type->tp_name, + PyString_AS_STRING(((PyEnumObject*)self)->ob_name), + ((PyEnumObject*)self)->ob_ival); +} + +PyObject* +PyEnumObject_name(PyObject* self) +{ + Py_INCREF(((PyEnumObject*)self)->ob_name); + return ((PyEnumObject*)self)->ob_name; +} + +} // extern "C" + +} // namespace Shiboken diff --git a/libshiboken/pyenum.h b/libshiboken/pyenum.h new file mode 100644 index 000000000..6fb9f52ef --- /dev/null +++ b/libshiboken/pyenum.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef PYENUM_H +#define PYENUM_H + +#include + +namespace Shiboken +{ + +extern "C" +{ + +typedef struct { + PyObject_HEAD + long ob_ival; + PyObject* ob_name; +} PyEnumObject; + +PyAPI_FUNC(PyObject*) PyEnumObject_repr(PyObject* self); +PyAPI_FUNC(PyObject*) PyEnumObject_name(PyObject* self); +PyAPI_FUNC(PyObject*) PyEnumObject_NonExtensibleNew(PyTypeObject* type, PyObject* args, PyObject* kwds); + +} // extern "C" + +PyObject* PyEnumObject_New(PyTypeObject *instanceType, + const char* item_name, + long item_value); +PyObject* PyEnumObject_New(PyTypeObject *instanceType, + PyObject* item_name, + long item_value); + +} // namespace Shiboken + +#endif // PYENUM_H + diff --git a/libshiboken/shiboken.h b/libshiboken/shiboken.h new file mode 100644 index 000000000..3f7e6781e --- /dev/null +++ b/libshiboken/shiboken.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. Please + * review the following information to ensure the GNU Lesser General + * Public License version 2.1 requirements will be met: + * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + * + * As a special exception to the GNU Lesser General Public License + * version 2.1, the object code form of a "work that uses the Library" + * may incorporate material from a header file that is part of the + * Library. You may distribute such object code under terms of your + * choice, provided that the incorporated material (i) does not exceed + * more than 5% of the total size of the Library; and (ii) is limited to + * numerical parameters, data structure layouts, accessors, macros, + * inline functions and templates. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef SHIBOKEN_H +#define SHIBOKEN_H + +#include +#include +#include +#include +#include +#include +#include + +#endif // SHIBOKEN_H + diff --git a/libshiboken/shiboken.pc.in b/libshiboken/shiboken.pc.in new file mode 100644 index 000000000..f120f00da --- /dev/null +++ b/libshiboken/shiboken.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@CMAKE_INSTALL_PREFIX@/lib +includedir=@CMAKE_INSTALL_PREFIX@/include/shiboken +generator_location=@CMAKE_INSTALL_PREFIX@/bin/shiboken + +Name: shiboken +Description: support library for Python bindings created with Shiboken generator. +Requires: Python +Version: @libshiboken_VERSION@ +Libs: -L${libdir} -lpython +Cflags: -I${includedir} + diff --git a/main.cpp b/main.cpp new file mode 100644 index 000000000..8700551f5 --- /dev/null +++ b/main.cpp @@ -0,0 +1,38 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include "headergenerator.h" +#include "cppgenerator.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); // needed by qxmlpatterns + + ApiExtractor extractor(argc, argv); + extractor.addGenerator(new HeaderGenerator); + extractor.addGenerator(new CppGenerator); + return extractor.exec(); +} + diff --git a/polymorphicdata.cpp b/polymorphicdata.cpp new file mode 100644 index 000000000..7d3a5b4c6 --- /dev/null +++ b/polymorphicdata.cpp @@ -0,0 +1,386 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include "polymorphicdata.h" +#include "shibokengenerator.h" + +// Prepare the information about polymorphic methods signatures +PolymorphicData::PolymorphicData(const AbstractMetaFunctionList overloads) + : m_minArgs(256), m_maxArgs(0), m_argType(0), + m_argPos(-1), m_headPolymorphicData(this) +{ + foreach (const AbstractMetaFunction* func, overloads) { + m_overloads.append(func); + int argSize = func->arguments().size(); + if (m_minArgs > argSize) + m_minArgs = argSize; + else if (m_maxArgs < argSize) + m_maxArgs = argSize; + PolymorphicData* currentPolymorphicData = this; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex() + 1)) + continue; + currentPolymorphicData = currentPolymorphicData->addPolymorphicData(func, arg->type()); + } + } + + // Fix minArgs + if (minArgs() > maxArgs()) + m_headPolymorphicData->m_minArgs = maxArgs(); +} + +PolymorphicData::PolymorphicData(PolymorphicData* headPolymorphicData, const AbstractMetaFunction* func, + const AbstractMetaType* argType, int argPos) + : m_minArgs(256), m_maxArgs(0), m_argType(argType), m_argPos(argPos), + m_headPolymorphicData(headPolymorphicData) +{ + if (func) + this->addPolymorphic(func); +} + +void PolymorphicData::addPolymorphic(const AbstractMetaFunction* func) +{ + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + + if (numArgs > m_headPolymorphicData->m_maxArgs) + m_headPolymorphicData->m_maxArgs = numArgs; + + if (numArgs < m_headPolymorphicData->m_minArgs) + m_headPolymorphicData->m_minArgs = numArgs; + + for (int i = 0; m_headPolymorphicData->m_minArgs > 0 && i < origNumArgs; i++) { + if (func->argumentRemoved(i + 1)) + continue; + if (!func->arguments()[i]->defaultValueExpression().isEmpty()) { + int fixedArgIndex = i - removed; + if (fixedArgIndex < m_headPolymorphicData->m_minArgs) + m_headPolymorphicData->m_minArgs = fixedArgIndex; + } + } + + m_overloads.append(func); +} + +PolymorphicData* PolymorphicData::addPolymorphicData(const AbstractMetaFunction* func, + const AbstractMetaType* argType) +{ + PolymorphicData* polymorphicData = 0; + foreach (PolymorphicData* tmp, m_nextPolymorphicData) { + // TODO: 'const char *', 'char *' and 'char' will have the same TypeEntry? + if (tmp->m_argType->typeEntry() == argType->typeEntry()) { + tmp->addPolymorphic(func); + polymorphicData = tmp; + continue; + } + } + + if (!polymorphicData) { + polymorphicData = new PolymorphicData(m_headPolymorphicData, func, argType, m_argPos + 1); + // The following code always put PyInt as the last element to be checked. + // This is useful to check the python argument as PyNumber instead of + // PyInt, but not getting in the way of other tipes of higher precision + // (e.g. PyFloat) + if (ShibokenGenerator::isPyInt(argType)) + m_nextPolymorphicData.append(polymorphicData); + else + m_nextPolymorphicData.prepend(polymorphicData); + } + + return polymorphicData; +} + +const AbstractMetaFunction* PolymorphicData::referenceFunction() const +{ + return m_overloads.at(0); +} + +const AbstractMetaArgument* PolymorphicData::argument(const AbstractMetaFunction* func) const +{ + if (isHeadPolymorphicData() || !m_overloads.contains(func)) + return 0; + + int argPos = 0; + int removed = 0; + for (int i = 0; argPos <= m_argPos; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + else + argPos++; + } + + return func->arguments()[m_argPos + removed]; +} + +PolymorphicDataList PolymorphicData::polymorphicDataOnPosition(PolymorphicData* polyData, int argPos) const +{ + PolymorphicDataList polyDataList; + if (polyData->argPos() == argPos) { + polyDataList.append(polyData); + } else if (polyData->argPos() < argPos) { + foreach (PolymorphicData* pd, polyData->nextPolymorphicData()) + polyDataList += polymorphicDataOnPosition(pd, argPos); + } + return polyDataList; +} + +PolymorphicDataList PolymorphicData::polymorphicDataOnPosition(int argPos) const +{ + PolymorphicDataList polyDataList; + polyDataList += polymorphicDataOnPosition(m_headPolymorphicData, argPos); + return polyDataList; +} + +bool PolymorphicData::nextArgumentHasDefaultValue() const +{ + foreach (PolymorphicData* polymorphicData, m_nextPolymorphicData) { + if (polymorphicData->hasDefaultValue()) + return true; + } + return false; +} + +bool PolymorphicData::isFinalOccurrence(const AbstractMetaFunction* func) const +{ + foreach (const PolymorphicData* pd, m_nextPolymorphicData) { + if (pd->overloads().contains(func)) + return false; + } + return true; +} + +bool PolymorphicData::hasDefaultValue() const +{ + foreach (const AbstractMetaFunction* func, m_overloads) { + if (!func->arguments()[m_argPos]->defaultValueExpression().isEmpty()) + return true; + } + return false; +} + +QList PolymorphicData::invalidArgumentLengths() const +{ + QSet validArgLengths; + foreach (const AbstractMetaFunction* func, m_headPolymorphicData->m_overloads) { + validArgLengths << func->arguments().size(); + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (!arg->defaultValueExpression().isEmpty()) + validArgLengths << arg->argumentIndex(); + } + } + + QList invalidArgLengths; + for (int i = minArgs() + 1; i < maxArgs(); i++) { + if (!validArgLengths.contains(i)) + invalidArgLengths.append(i); + } + + return invalidArgLengths; +} + +int PolymorphicData::numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos) +{ + int removed = 0; + if (finalArgPos < 0) + finalArgPos = func->arguments().size(); + for (int i = 0; i < finalArgPos; i++) { + if (func->argumentRemoved(i + 1)) + removed++; + } + return removed; +} + +QPair PolymorphicData::getMinMaxArguments(const AbstractMetaFunctionList overloads) +{ + int minArgs = 10000; + int maxArgs = 0; + for (int i = 0; i < overloads.size(); i++) { + const AbstractMetaFunction* func = overloads[i]; + int origNumArgs = func->arguments().size(); + int removed = numberOfRemovedArguments(func); + int numArgs = origNumArgs - removed; + if (maxArgs < numArgs) + maxArgs = numArgs; + if (minArgs > numArgs) + minArgs = numArgs; + for (int j = 0; j < origNumArgs; j++) { + if (func->argumentRemoved(j + 1)) + continue; + int fixedArgIndex = j - removed; + if (fixedArgIndex < minArgs && !func->arguments()[j]->defaultValueExpression().isEmpty()) + minArgs = fixedArgIndex; + } + } + return QPair(minArgs, maxArgs); +} + +void PolymorphicData::dumpGraph(QString filename) const +{ + QFile file(filename); + if (file.open(QFile::WriteOnly)) { + QTextStream s(&file); + s << dumpGraph(m_headPolymorphicData); + } +} + +QString PolymorphicData::dumpGraph(const PolymorphicData* polyData) const +{ + if (!polyData) + return QString(); + + QString indent(4, ' '); + QString result; + QTextStream s(&result); + if (polyData->m_argPos == -1) { + const AbstractMetaFunction* rfunc = polyData->referenceFunction(); + s << "digraph PolymorphicFunction {" << endl; + s << indent << "graph [fontsize=12 fontname=freemono labelloc=t splines=true overlap=false rankdir=LR];" << endl; + + // Shows all function signatures + s << "legend [fontsize=9 fontname=freemono shape=rect label=\""; + foreach (const AbstractMetaFunction* func, polyData->overloads()) { + s << "f" << functionNumber(func) << " : "; + if (func->type()) + s << func->type()->cppSignature().replace('<', "<").replace('>', ">"); + else + s << "void"; + s << ' ' << func->minimalSignature().replace('<', "<").replace('>', ">") << "\\l"; + } + s << "\"];" << endl; + + // Function box title + s << indent << '"' << rfunc->name() << "\" [shape=plaintext style=\"filled,bold\" margin=0 fontname=freemono fillcolor=white penwidth=1 "; + s << "label=<"; + s << ""; + + // Function return type + s << ""; + + // Shows type changes for all function signatures + foreach (const AbstractMetaFunction* func, polyData->overloads()) { + if (func->typeReplaced(0).isEmpty()) + continue; + s << ""; + } + + // Minimum and maximum number of arguments + s << ""; + s << ""; + + if (rfunc->ownerClass()) { + if (rfunc->implementingClass() != rfunc->ownerClass()) + s << ""; + if (rfunc->declaringClass() != rfunc->ownerClass() && rfunc->declaringClass() != rfunc->implementingClass()) + s << ""; + } + + // Overloads for the signature to present point + s << ""; + + s << "
    "; + if (rfunc->ownerClass()) + s << rfunc->ownerClass()->name() << "::"; + s << rfunc->name().replace('<', "<").replace('>', ">") << ""; + if (rfunc->isVirtual()) { + s << "
    <<"; + if (rfunc->isAbstract()) + s << "pure "; + s << "virtual>>"; + } + s << "
    original type"; + if (rfunc->type()) + s << rfunc->type()->cppSignature().replace('<', "<").replace('>', ">"); + else + s << "void"; + s << "
    f" << functionNumber(func); + s << "-type"; + s << func->typeReplaced(0).replace('<', "<").replace('>', ">") << "
    minArgs"; + s << minArgs() << "
    maxArgs"; + s << maxArgs() << "
    implementor" << rfunc->implementingClass()->name() << "
    declarator" << rfunc->declaringClass()->name() << "
    overloads"; + foreach (const AbstractMetaFunction* func, polyData->overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "
    > ];" << endl; + + foreach (const PolymorphicData* pd, polyData->nextPolymorphicData()) + s << indent << '"' << rfunc->name() << "\" -> " << dumpGraph(pd); + + s << "}" << endl; + } else { + QString argId = QString("arg_%1").arg((long)polyData); + s << argId << ';' << endl; + + s << indent << '"' << argId << "\" [shape=\"plaintext\" style=\"filled,bold\" margin=\"0\" fontname=\"freemono\" fillcolor=\"white\" penwidth=1 "; + s << "label=<"; + + // Argument box title + s << ""; + + // Argument type information + s << ""; + + // Overloads for the signature to present point + s << ""; + + // Show default values (original and modified) for various functions + foreach (const AbstractMetaFunction* func, polyData->overloads()) { + const AbstractMetaArgument* arg = polyData->argument(func); + if (!arg) + continue; + if (!arg->defaultValueExpression().isEmpty() || + arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { + s << ""; + } + if (arg->defaultValueExpression() != arg->originalDefaultValueExpression()) { + s << ""; + } + } + + s << "
    "; + s << "arg #" << polyData->argPos() << "
    type"; + s << polyData->argType()->cppSignature().replace("&", "&") << "
    overloads"; + foreach (const AbstractMetaFunction* func, polyData->overloads()) + s << 'f' << functionNumber(func) << ' '; + s << "
    f" << functionNumber(func); + s << "-default"; + s << arg->defaultValueExpression() << "
    f" << functionNumber(func); + s << "-orig-default"; + s << arg->originalDefaultValueExpression() << "
    >];" << endl; + + foreach (const PolymorphicData* pd, polyData->nextPolymorphicData()) + s << indent << argId << " -> " << dumpGraph(pd); + } + return result; +} + +int PolymorphicData::functionNumber(const AbstractMetaFunction* func) const +{ + m_headPolymorphicData->m_overloads.indexOf(func); +} + +PolymorphicData::~PolymorphicData() +{ + while (!m_nextPolymorphicData.isEmpty()) + delete m_nextPolymorphicData.takeLast(); +} diff --git a/polymorphicdata.h b/polymorphicdata.h new file mode 100644 index 000000000..1e4741902 --- /dev/null +++ b/polymorphicdata.h @@ -0,0 +1,87 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef POLYMORPHICDATA_H +#define POLYMORPHICDATA_H + +#include +#include +#include + +class PolymorphicData; +typedef QList PolymorphicDataList; + +class PolymorphicData +{ +public: + PolymorphicData(const AbstractMetaFunctionList overloads); + + int minArgs() const { return m_headPolymorphicData->m_minArgs; } + int maxArgs() const { return m_headPolymorphicData->m_maxArgs; } + int argPos() const { return m_argPos; } + + const AbstractMetaType* argType() const { return m_argType; } + const AbstractMetaFunction* referenceFunction() const; + const AbstractMetaArgument* argument(const AbstractMetaFunction* func) const; + PolymorphicDataList polymorphicDataOnPosition(int argPos) const; + + bool isHeadPolymorphicData() const { return this == m_headPolymorphicData; } + bool hasDefaultValue() const; + bool nextArgumentHasDefaultValue() const; + bool isFinalOccurrence(const AbstractMetaFunction* func) const; + + QList overloads() const { return m_overloads; } + PolymorphicDataList nextPolymorphicData() const { return m_nextPolymorphicData; } + + QList invalidArgumentLengths() const; + + static int numberOfRemovedArguments(const AbstractMetaFunction* func, int finalArgPos = -1); + static QPair getMinMaxArguments(const AbstractMetaFunctionList overloads); + + void dumpGraph(QString filename) const; + + ~PolymorphicData(); + +private: + PolymorphicData(PolymorphicData* headPolymorphicData, const AbstractMetaFunction* func, + const AbstractMetaType* argType, int argPos); + + void addPolymorphic(const AbstractMetaFunction* func); + PolymorphicData* addPolymorphicData(const AbstractMetaFunction* func, const AbstractMetaType* argType); + + QString dumpGraph(const PolymorphicData* polyData) const; + int functionNumber(const AbstractMetaFunction* func) const; + PolymorphicDataList polymorphicDataOnPosition(PolymorphicData* polyData, int argPos) const; + + int m_minArgs; + int m_maxArgs; + int m_argPos; + const AbstractMetaType* m_argType; + QList m_overloads; + + PolymorphicData* m_headPolymorphicData; + PolymorphicDataList m_nextPolymorphicData; +}; + + +#endif // POLYMORPHICDATA_H diff --git a/shibokengenerator.cpp b/shibokengenerator.cpp new file mode 100644 index 000000000..89b5e7b4e --- /dev/null +++ b/shibokengenerator.cpp @@ -0,0 +1,829 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "shibokengenerator.h" +#include + +#include +#include + +#define NULL_VALUE "NULL" +#define COMMENT_LINE_WIDTH 77 + +static Indentor INDENT; +static void dumpFunction(AbstractMetaFunctionList lst); + +QHash ShibokenGenerator::m_pythonPrimitiveTypeName = QHash(); +QHash ShibokenGenerator::m_pythonOperators = QHash(); +QHash ShibokenGenerator::m_formatUnits = QHash(); + +ShibokenGenerator::ShibokenGenerator() : Generator() +{ + if (m_pythonPrimitiveTypeName.isEmpty()) + ShibokenGenerator::initPrimitiveTypesCorrespondences(); +} + +void ShibokenGenerator::initPrimitiveTypesCorrespondences() +{ + // Python primitive types names + m_pythonPrimitiveTypeName.clear(); + + // PyBool + m_pythonPrimitiveTypeName["bool"] = "PyBool"; + + // PyInt + m_pythonPrimitiveTypeName["char"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned char"] = "PyInt"; + m_pythonPrimitiveTypeName["int"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned int"] = "PyInt"; + m_pythonPrimitiveTypeName["short"] = "PyInt"; + m_pythonPrimitiveTypeName["unsigned short"] = "PyInt"; + m_pythonPrimitiveTypeName["long"] = "PyInt"; + + // PyFloat + m_pythonPrimitiveTypeName["double"] = "PyFloat"; + m_pythonPrimitiveTypeName["float"] = "PyFloat"; + + // PyLong + m_pythonPrimitiveTypeName["unsigned long"] = "PyLong"; + m_pythonPrimitiveTypeName["long long"] = "PyLong"; + m_pythonPrimitiveTypeName["__int64"] = "PyLong"; + m_pythonPrimitiveTypeName["unsigned long long"] = "PyLong"; + m_pythonPrimitiveTypeName["unsigned __int64"] = "PyLong"; + + // Python operators + m_pythonOperators.clear(); + + // Arithmetic operators + m_pythonOperators["operator+"] = "add"; + m_pythonOperators["operator-"] = "sub"; + m_pythonOperators["operator*"] = "mul"; + m_pythonOperators["operator/"] = "div"; + m_pythonOperators["operator%"] = "mod"; + + // Inplace arithmetic operators + m_pythonOperators["operator+="] = "iadd"; + m_pythonOperators["operator-="] = "isub"; + m_pythonOperators["operator*="] = "imul"; + m_pythonOperators["operator/="] = "idiv"; + m_pythonOperators["operator%="] = "imod"; + + // Bitwise operators + m_pythonOperators["operator&"] = "and"; + m_pythonOperators["operator^"] = "xor"; + m_pythonOperators["operator|"] = "or"; + m_pythonOperators["operator<<"] = "lshift"; + m_pythonOperators["operator>>"] = "rshift"; + m_pythonOperators["operator~"] = "invert"; + + // Inplace bitwise operators + m_pythonOperators["operator&="] = "iand"; + m_pythonOperators["operator^="] = "ixor"; + m_pythonOperators["operator|="] = "ior"; + m_pythonOperators["operator<<="] = "ilshift"; + m_pythonOperators["operator>>="] = "irshift"; + + // Comparison operators + m_pythonOperators["operator=="] = "eq"; + m_pythonOperators["operator!="] = "ne"; + m_pythonOperators["operator<"] = "lt"; + m_pythonOperators["operator>"] = "gt"; + m_pythonOperators["operator<="] = "le"; + m_pythonOperators["operator>="] = "ge"; + + // Initialize format units for C++->Python->C++ conversion + m_formatUnits.clear(); + m_formatUnits.insert("bool", "i"); + m_formatUnits.insert("char", "b"); + m_formatUnits.insert("unsigned char", "B"); + m_formatUnits.insert("int", "i"); + m_formatUnits.insert("unsigned int", "I"); + m_formatUnits.insert("short", "h"); + m_formatUnits.insert("unsigned short", "H"); + m_formatUnits.insert("long", "l"); + m_formatUnits.insert("unsigned long", "k"); + m_formatUnits.insert("long long", "L"); + m_formatUnits.insert("__int64", "L"); + m_formatUnits.insert("unsigned long long", "K"); + m_formatUnits.insert("unsigned __int64", "K"); + m_formatUnits.insert("double", "d"); + m_formatUnits.insert("float", "f"); +} + +FunctionModificationList ShibokenGenerator::functionModifications(const AbstractMetaFunction* func) +{ + FunctionModificationList mods; + const AbstractMetaClass *cls = func->ownerClass(); + while (cls) { + mods += func->modifications(cls); + if (cls == cls->baseClass()) + break; + cls = cls->baseClass(); + } + return mods; +} + +QString ShibokenGenerator::translateType(const AbstractMetaType* cType, + const AbstractMetaClass* context, + int option) const +{ + QString s; + + if (context != 0 + && cType != 0 + && context->typeEntry()->isGenericClass() + && cType->originalTemplateType()) + { + cType = cType->originalTemplateType(); + } + + if (!cType) { + s = "void"; + } else if (cType->isArray()) { + s = translateType(cType->arrayElementType(), context) + "[]"; + } else if (cType->isEnum() || cType->isFlags()) { + if (option & Generator::EnumAsInts) + s = "int"; + else + s = cType->cppSignature(); + } else if (cType->isValue() || cType->isObject() || cType->isReference()) { + s = cType->typeEntry()->qualifiedCppName(); + if (cType->isObject()) + s.append('*'); + } else { + s = cType->cppSignature(); + if (cType->isConstant() && (option & Generator::ExcludeConst)) + s.replace("const", ""); + if (cType->isReference() && (option & Generator::ExcludeReference)) + s.replace("&", ""); + } + + return s; +} + +QString ShibokenGenerator::wrapperName(const AbstractMetaClass* metaClass) +{ + QString result = metaClass->name(); + if (metaClass->enclosingClass()) // is a inner class + result.replace("::", "_"); + result +="Wrapper"; + return result; +} + +QString ShibokenGenerator::cpythonFunctionName(const AbstractMetaFunction* func) +{ + QString result = QLatin1String("Py"); + + if (func->ownerClass()) { + result += func->ownerClass()->name(); + if (func->ownerClass()->enclosingClass()) // is a inner class + result.replace("::", "_"); + + result += '_'; + + if (func->isConstructor() || func->isCopyConstructor()) + result += "New"; + else if (func->isOperatorOverload()) + result += ShibokenGenerator::pythonOperatorFunctionName(func); + else + result += func->name(); + } else { + result += moduleName() + "Module_" + func->name(); + } + + return result; +} + +QString ShibokenGenerator::cpythonEnumName(const EnumTypeEntry* enumEntry) +{ + QString result = QString("Py") + moduleName() + '_' + + enumEntry->qualifiedCppName(); + result.replace("::", "_"); + return result; +} + +QString ShibokenGenerator::getFunctionReturnType(const AbstractMetaFunction* func) const +{ + if (func->ownerClass() && (func->isConstructor() || func->isCopyConstructor())) + return func->ownerClass()->qualifiedCppName() + '*'; + + return translateType(func->type(), func->implementingClass()); + + //TODO: check these lines + //QString modifiedReturnType = QString(func->typeReplaced(0)); + //return modifiedReturnType.isNull() ? + //translateType(func->type(), func->implementingClass()) : modifiedReturnType; +} + +QString ShibokenGenerator::writeBaseConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context) +{ + QString typeName; + if (type->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type->typeEntry(); + if (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + typeName = ptype->name(); + } else { + typeName = translateType(type, context); + } + s << "Shiboken::Converter< " << typeName << " >::"; + return typeName; +} + +void ShibokenGenerator::writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, QString argumentName) +{ + QString typeName = writeBaseConversion(s, type, context); + s << "toPython(Shiboken::ValueHolder< " << typeName << " >(" << argumentName << "))"; +} + +void ShibokenGenerator::writeToCppConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, QString argumentName) +{ + writeBaseConversion(s, type, context); + s << "toCpp(" << argumentName << ')'; +} + +QString ShibokenGenerator::getFormatUnitString(const AbstractMetaArgumentList arguments) const +{ + QString result; + foreach (const AbstractMetaArgument *arg, arguments) { + if ((arg->type()->isQObject() || arg->type()->isObject() || arg->type()->isValue())) { // && +// !arg->type()->isReference()) { + result += "O&"; + } else if (arg->type()->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) arg->type()->typeEntry(); + if (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + result += m_formatUnits[ptype->name()]; + } else if (arg->type()->isNativePointer() && arg->type()->name() == "char") { + result += 'z'; + } else { + result += 'Y'; + } + } + return result; +} + +QString ShibokenGenerator::cpythonBaseName(const AbstractMetaType* type) +{ + if (type->name() == "char" && type->isNativePointer()) + return QString("PyString"); + return cpythonBaseName(type->typeEntry()); +} + +QString ShibokenGenerator::cpythonBaseName(const TypeEntry* type) +{ + QString baseName; + if ((type->isObject() || type->isValue())) { // && !type->isReference()) { + baseName = QString("Py") + type->name(); + } else if (type->isPrimitive()) { + const PrimitiveTypeEntry* ptype = (const PrimitiveTypeEntry*) type; + if (ptype->basicAliasedTypeEntry()) + ptype = ptype->basicAliasedTypeEntry(); + if (ptype->targetLangApiName() == ptype->name()) + baseName = m_pythonPrimitiveTypeName[ptype->name()]; + else + baseName = ptype->targetLangApiName(); + } else if (type->isEnum()) { + baseName = cpythonEnumName((const EnumTypeEntry*) type); + } else if (type->isContainer()) { + const ContainerTypeEntry* ctype = (const ContainerTypeEntry*) type; + switch (ctype->type()) { + case ContainerTypeEntry::ListContainer: + case ContainerTypeEntry::StringListContainer: + case ContainerTypeEntry::LinkedListContainer: + case ContainerTypeEntry::VectorContainer: + case ContainerTypeEntry::StackContainer: + case ContainerTypeEntry::QueueContainer: + baseName = "PyList"; + break; + case ContainerTypeEntry::PairContainer: + baseName = "PyTuple"; + break; + case ContainerTypeEntry::SetContainer: + baseName = "PySet"; + break; + case ContainerTypeEntry::MapContainer: + case ContainerTypeEntry::MultiMapContainer: + case ContainerTypeEntry::HashContainer: + case ContainerTypeEntry::MultiHashContainer: + baseName = "PyDict"; + break; + } + } else { + baseName = "PyObject"; + } + return baseName; +} + +QString ShibokenGenerator::cpythonTypeName(const TypeEntry* type) +{ + return cpythonBaseName(type) + "_Type"; +} + +QString ShibokenGenerator::cpythonOperatorFunctionName(const AbstractMetaFunction* func) +{ + if (!func->isOperatorOverload()) + return QString(); + return QString("Py") + func->ownerClass()->name() + + '_' + pythonOperatorFunctionName(func->originalName()); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(QString cppTypeName) +{ + return ShibokenGenerator::m_pythonPrimitiveTypeName.value(cppTypeName, QString()); +} + +QString ShibokenGenerator::pythonPrimitiveTypeName(const PrimitiveTypeEntry* type) +{ + if (type->basicAliasedTypeEntry()) + type = type->basicAliasedTypeEntry(); + return pythonPrimitiveTypeName(type->name()); +} + +QString ShibokenGenerator::pythonOperatorFunctionName(const AbstractMetaFunction* func) +{ + QString op = pythonOperatorFunctionName(func->originalName()); + if (func->arguments().isEmpty()) { + if (op == "__sub__") + op = QString("__neg__"); + else if (op == "__add__") + op = QString("__pos__"); + } else if (func->isStatic() && func->arguments().size() == 2) { + // If a operator overload function has 2 arguments and + // is static we assume that it is a reverse operator. + op = op.insert(2, 'r'); + } + return op; +} + +bool ShibokenGenerator::isNumber(QString cpythonApiName) +{ + return cpythonApiName == "PyInt" + || cpythonApiName == "PyFloat" + || cpythonApiName == "PyLong"; +} + +bool ShibokenGenerator::isNumber(const TypeEntry* type) +{ + if (!type->isPrimitive()) + return false; + return isNumber(pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type)); +} + +bool ShibokenGenerator::isNumber(const AbstractMetaType* type) +{ + return isNumber(type->typeEntry()); +} + +bool ShibokenGenerator::isPyInt(const TypeEntry* type) +{ + if (!type->isPrimitive()) + return false; + return pythonPrimitiveTypeName((const PrimitiveTypeEntry*) type) == "PyInt"; +} + +bool ShibokenGenerator::isPyInt(const AbstractMetaType* type) +{ + return isPyInt(type->typeEntry()); +} + +bool ShibokenGenerator::isReverseOperator(const AbstractMetaFunction* func) +{ + if (!func->isOperatorOverload()) + return false; + + const AbstractMetaClass* cppClass = func->ownerClass(); + AbstractMetaArgumentList args = func->arguments(); + // Here we expect static operator overloads with + // 2 arguments to represent reverse operators. + // e.g. static operator*(double,TYPE) => double * TYPE => TYPE.__rmul__(double). + return args.size() == 2 && cppClass && + args[1]->type()->typeEntry() == cppClass->typeEntry(); +} + +static QString checkFunctionName(QString baseName, bool genericNumberType) +{ + if (genericNumberType && ShibokenGenerator::isNumber(baseName)) + baseName = "PyNumber"; + return baseName + "_Check"; +} + +QString ShibokenGenerator::cpythonCheckFunction(const AbstractMetaType* type, bool genericNumberType) +{ + return checkFunctionName(cpythonBaseName(type), genericNumberType); +} + +QString ShibokenGenerator::cpythonCheckFunction(const TypeEntry* type, bool genericNumberType) +{ + return checkFunctionName(cpythonBaseName(type), genericNumberType); +} + +QString ShibokenGenerator::argumentString(const AbstractMetaFunction *func, + const AbstractMetaArgument *argument, + uint options) const +{ + QString modified_type = func->typeReplaced(argument->argumentIndex() + 1); + QString arg; + + if (modified_type.isEmpty()) + arg = translateType(argument->type(), func->implementingClass(), (Generator::Option) options); + else + arg = modified_type.replace('$', '.'); + + if (!(options & Generator::SkipName)) { + arg += " "; + arg += argument->argumentName(); + } + + QList referenceCounts; + referenceCounts = func->referenceCounts(func->implementingClass(), argument->argumentIndex() + 1); + if ((options & Generator::SkipDefaultValues) != Generator::SkipDefaultValues && + !argument->originalDefaultValueExpression().isEmpty()) + { + QString default_value = argument->originalDefaultValueExpression(); + if (default_value == "NULL") + default_value = NULL_VALUE; + + //WORKAROUND: fix this please + if (default_value.startsWith("new ")) + default_value.remove(0, 4); + + arg += " = " + default_value; + } + + return arg; +} + +void ShibokenGenerator::writeArgument(QTextStream &s, + const AbstractMetaFunction *func, + const AbstractMetaArgument *argument, + uint options) const +{ + s << argumentString(func, argument, options); +} + +void ShibokenGenerator::writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *func, + uint options) const +{ + AbstractMetaArgumentList arguments = func->arguments(); + + if (options & Generator::WriteSelf) { + s << func->implementingClass()->name() << '&'; + if (!(options & SkipName)) + s << " self"; + } + + int argUsed = 0; + for (int i = 0; i < arguments.size(); ++i) { + if ((options & Generator::SkipRemovedArguments) && func->argumentRemoved(i+1)) + continue; + + if ((options & Generator::WriteSelf) || argUsed != 0) + s << ", "; + writeArgument(s, func, arguments[i], options); + argUsed++; + } +} + +QString ShibokenGenerator::functionReturnType(const AbstractMetaFunction* func, int option) const +{ + QString modifiedReturnType = QString(func->typeReplaced(0)); + if (!modifiedReturnType.isNull() && !(option & OriginalTypeDescription)) + return modifiedReturnType; + else + return translateType(func->type(), func->implementingClass(), option); +} + +QString ShibokenGenerator::functionSignature(const AbstractMetaFunction *func, + QString prepend, + QString append, + int option, + int argCount) const +{ + AbstractMetaArgumentList arguments = func->arguments(); + int argumentCount = argCount < 0 ? arguments.size() : argCount; + + QString result; + QTextStream s(&result); + // The actual function + if (!(func->isEmptyFunction() || + func->isNormal() || + func->isSignal())) { + option = Option(option | Generator::SkipReturnType); + } else { + s << functionReturnType(func, option) << ' '; + } + + // name + QString name(func->originalName()); + if (func->isConstructor()) + name = wrapperName(func->ownerClass()); + + s << prepend << name << append << '('; + writeFunctionArguments(s, func, option); + s << ')'; + + if (func->isConstant() && !(option & Generator::ExcludeMethodConst)) + s << " const"; + + return result; +} + +QString ShibokenGenerator::signatureForDefaultVirtualMethod(const AbstractMetaFunction *func, + QString prepend, + QString append, + int option, + int argCount) const +{ + QString defaultMethodSignature = functionSignature(func, prepend, append, option, argCount); + QString staticSelf("("); + if (func->isConstant()) + staticSelf += "const "; + staticSelf += func->ownerClass()->qualifiedCppName()+"& self"; + if (!func->arguments().isEmpty()) + staticSelf += ", "; + defaultMethodSignature.replace(defaultMethodSignature.lastIndexOf(") const"), 7, ")"); + defaultMethodSignature.replace(defaultMethodSignature.indexOf('('), 1, staticSelf); + return defaultMethodSignature; +} + +bool ShibokenGenerator::hasInjectedCodeOrSignatureModification(const AbstractMetaFunction* func) +{ + foreach (FunctionModification mod, functionModifications(func)) { + if (mod.isCodeInjection() || mod.isRenameModifier()) + return true; + } + return false; +} + +void ShibokenGenerator::writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *func, + uint options) const +{ + AbstractMetaArgumentList arguments = func->arguments(); + int argCount = 0; + for (int j = 0, max = arguments.size(); j < max; j++) { + + if ((options & Generator::SkipRemovedArguments) && + (func->argumentRemoved(arguments.at(j)->argumentIndex() +1))) + continue; + + if (argCount > 0) + s << ", "; + + QString argName; + + if ((options & Generator::BoxedPrimitive) && + !arguments.at(j)->type()->isReference() && + (arguments.at(j)->type()->isQObject() || + arguments.at(j)->type()->isObject())) { + //s << "brian::wrapper_manager::instance()->retrieve( " << arguments.at(j)->argumentName() << " )"; + // TODO: replace boost thing + s << "python::ptr( " << arguments.at(j)->argumentName() << " )"; + } else { + s << arguments.at(j)->argumentName(); + } + argCount++; + } +} + +AbstractMetaFunctionList ShibokenGenerator::queryGlobalOperators(const AbstractMetaClass *metaClass) +{ + AbstractMetaFunctionList result; + + foreach (AbstractMetaFunction *func, metaClass->functions()) { + if (func->isInGlobalScope() && func->isOperatorOverload()) + result.append(func); + } + return result; +} + +AbstractMetaFunctionList ShibokenGenerator::sortContructor(AbstractMetaFunctionList list) +{ + AbstractMetaFunctionList result; + + foreach (AbstractMetaFunction *func, list) { + bool inserted = false; + foreach (AbstractMetaArgument *arg, func->arguments()) { + if (arg->type()->isFlags() || arg->type()->isEnum()) { + result.push_back(func); + inserted = true; + break; + } + } + if (!inserted) + result.push_front(func); + } + + return result; +} + +AbstractMetaFunctionList ShibokenGenerator::queryFunctions(const AbstractMetaClass *metaClass, bool allFunctions) +{ + AbstractMetaFunctionList result; + + if (allFunctions) { + int defaultFlags = AbstractMetaClass::NormalFunctions | AbstractMetaClass::Visible; + defaultFlags |= metaClass->isInterface() ? 0 : AbstractMetaClass::ClassImplements; + + // Constructors + result = metaClass->queryFunctions(AbstractMetaClass::Constructors + | defaultFlags); + + // put enum constructor first to avoid conflict with int contructor + result = sortContructor(result); + + // Final functions + result += metaClass->queryFunctions(AbstractMetaClass::FinalInTargetLangFunctions + | AbstractMetaClass::NonStaticFunctions + | defaultFlags); + + //virtual + result += metaClass->queryFunctions(AbstractMetaClass::VirtualInTargetLangFunctions + | AbstractMetaClass::NonStaticFunctions + | defaultFlags); + + // Static functions + result += metaClass->queryFunctions(AbstractMetaClass::StaticFunctions | defaultFlags); + + // Empty, private functions, since they aren't caught by the other ones + result += metaClass->queryFunctions(AbstractMetaClass::Empty + | AbstractMetaClass::Invisible + | defaultFlags); + // Signals + result += metaClass->queryFunctions(AbstractMetaClass::Signals | defaultFlags); + } else { + result = metaClass->functionsInTargetLang(); + } + + return result; +} + +void ShibokenGenerator::writeFunctionCall(QTextStream& s, + const AbstractMetaFunction* func, + uint options) const +{ + if (!(options & Generator::SkipName)) + s << (func->isConstructor() ? func->ownerClass()->qualifiedCppName() : func->originalName()); + s << '('; + writeArgumentNames(s, func, options); + s << ')'; +} + +AbstractMetaFunctionList ShibokenGenerator::filterFunctions(const AbstractMetaClass* metaClass) +{ + AbstractMetaFunctionList lst = queryFunctions(metaClass, true); + foreach (AbstractMetaFunction *func, lst) { + //skip signals + if (func->isSignal() + || func->isDestructor() + || (func->isModifiedRemoved() && !func->isAbstract())) + lst.removeOne(func); + } + + //virtual not implemented in current class + AbstractMetaFunctionList virtualLst = metaClass->queryFunctions(AbstractMetaClass::VirtualFunctions); + foreach (AbstractMetaFunction* func, virtualLst) { + if ((func->implementingClass() != metaClass) && !lst.contains(func)) + lst.append(func); + } + + //append global operators + lst += queryGlobalOperators(metaClass); + + return lst; + //return metaClass->functions(); +} + +CodeSnipList ShibokenGenerator::getCodeSnips(const AbstractMetaFunction *func) +{ + CodeSnipList result; + const AbstractMetaClass* metaClass = func->implementingClass(); + while (!metaClass) { + foreach (FunctionModification mod, func->modifications(metaClass)) { + if (mod.isCodeInjection()) + result << mod.snips; + } + + if (metaClass == metaClass->baseClass()) + break; + metaClass = metaClass->baseClass(); + } + + return result; +} + +void ShibokenGenerator::writeCodeSnips(QTextStream& s, + const CodeSnipList& codeSnips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaFunction* func) +{ + foreach (CodeSnip snip, codeSnips) { + if ((snip.position != position) || !(snip.language & language)) + continue; + + QString code; + QTextStream tmpStream(&code); + Indentation indent1(INDENT); + Indentation indent2(INDENT); + snip.formattedCode(tmpStream, INDENT); + + if (func) { + // replace template variable for return variable name + code.replace("%0", retvalVariableName()); + + // replace template variables for individual arguments + int removed = 0; + for (int i = 0; i < func->arguments().size(); i++) { + if (func->argumentRemoved(i+1)) + removed++; + code.replace("%" + QString::number(i+1), QString("cpp_arg%1").arg(i - removed)); + } + + // replace template variables for not removed arguments + int i = 0; + QString argumentNames; + foreach (const AbstractMetaArgument* arg, func->arguments()) { + if (func->argumentRemoved(arg->argumentIndex()+1)) + continue; + if (i > 0) + argumentNames += ", "; + argumentNames += QString("cpp_arg%1").arg(i++); + } + code.replace("%ARGUMENT_NAMES", argumentNames); + + replaceTemplateVariables(code, func); + } + + s << code; + } +} + +bool ShibokenGenerator::canCreateWrapperFor(const AbstractMetaClass* metaClass) +{ + return !metaClass->hasPrivateDestructor(); +} + +QStringList ShibokenGenerator::getBaseClasses(const AbstractMetaClass* metaClass) +{ + QStringList baseClass; + + if (!metaClass->baseClassName().isEmpty() && + (metaClass->name() != metaClass->baseClassName())) + baseClass.append(metaClass->baseClassName()); + + foreach (AbstractMetaClass* interface, metaClass->interfaces()) { + AbstractMetaClass* aux = interface->primaryInterfaceImplementor(); + if (!aux) + continue; + + //skip templates + if (!aux->templateArguments().isEmpty()) + continue; + + if (!aux->name().isEmpty() && (metaClass->name() != aux->name())) + baseClass.append(aux->name()); + } + + return baseClass; +} + +static void dumpFunction(AbstractMetaFunctionList lst) +{ + qDebug() << "DUMP FUNCTIONS: "; + foreach (AbstractMetaFunction *func, lst) + qDebug() << "*" << func->ownerClass()->name() + << func->signature() + << "Private: " << func->isPrivate() + << "Empty: " << func->isEmptyFunction() + << "Static:" << func->isStatic() + << "Signal:" << func->isSignal() + << "ClassImplements: " << (func->ownerClass() != func->implementingClass()) + << "is operator:" << func->isOperatorOverload() + << "is global:" << func->isInGlobalScope(); +} diff --git a/shibokengenerator.h b/shibokengenerator.h new file mode 100644 index 000000000..f9cd564f7 --- /dev/null +++ b/shibokengenerator.h @@ -0,0 +1,213 @@ +/* + * This file is part of the Shiboken Python Binding Generator project. + * + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SHIBOKENGENERATOR_H +#define SHIBOKENGENERATOR_H + +#include +#include + +class DocParser; + +/** + * Abstract generator that contains common methods used in CppGenerator and HeaderGenerator. + */ +class ShibokenGenerator : public Generator +{ +public: + ShibokenGenerator(); + + /** + * Translate metatypes to C++ format. + * \param metaType a pointer to metatype + * \param context the current meta class + * \param option some extra options + * \return the metatype translated to C++ format + */ + virtual QString translateType(const AbstractMetaType* metaType, + const AbstractMetaClass* context, + int option = NoOption) const; + /** + * Write a function argument in the C++ in the text stream \p s. + * This function just call \code s << argumentString(); \endcode + * \param s text stream used to write the output. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + void writeArgument(QTextStream &s, + const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + uint options = 0) const; + /** + * Create a QString in the C++ format to an function argument. + * \param func the current metafunction. + * \param argument metaargument information to be parsed. + * \param options some extra options. + */ + QString argumentString(const AbstractMetaFunction* func, + const AbstractMetaArgument* argument, + uint options = 0) const; + + void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction* func, + uint options = 0) const; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param func the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction* func, + uint options = 0) const; + QString functionReturnType(const AbstractMetaFunction* func, int option = NoOption) const; + /** + * Write a code snip into the buffer \p s. + * CodeSnip are codes inside inject-code tags. + * \param s the buffer + * \param func the cpp function + * \param code_snips a list of code snips + * \param position the position to insert the code snip + * \param language the kind of code snip + */ + void writeCodeSnips(QTextStream &s, + const CodeSnipList &code_snips, + CodeSnip::Position position, + TypeSystem::Language language, + const AbstractMetaFunction* func = 0); + /// returns the code snips of a function + CodeSnipList getCodeSnips(const AbstractMetaFunction* func); + static bool canCreateWrapperFor(const AbstractMetaClass* metaClass); + /** + * Function witch parse the metafunction information + * \param func the function witch will be parserd + * \param option some extra options + * \param arg_count the number of function arguments + */ + QString functionSignature(const AbstractMetaFunction* func, + QString prepend = "", + QString append = "", + int option = NoOption, + int arg_count = -1) const; + + QString signatureForDefaultVirtualMethod(const AbstractMetaFunction* func, + QString prepend = "", + QString append = "_default", + int option = NoOption, + int arg_count = -1) const; + + virtual QString subDirectoryForClass(const AbstractMetaClass* metaClass) const + { + return subDirectoryForPackage(metaClass->package()); + } + + bool hasInjectedCodeOrSignatureModification(const AbstractMetaFunction* func); + QStringList getBaseClasses(const AbstractMetaClass* metaClass); + + QString writeBaseConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context); + void writeToPythonConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, QString argumentName); + void writeToCppConversion(QTextStream& s, const AbstractMetaType* type, + const AbstractMetaClass* context, QString argumentName); + + static QString wrapperName(const AbstractMetaClass* metaClass); + + static QString pythonPrimitiveTypeName(QString cppTypeName); + static QString pythonPrimitiveTypeName(const PrimitiveTypeEntry* type); + + static QString pythonOperatorFunctionName(QString cppOpFuncName) + { + return QString("__%1__").arg(m_pythonOperators.value(cppOpFuncName)); + } + static QString pythonOperatorFunctionName(const AbstractMetaFunction* func); + static QString pythonRichCompareOperatorId(QString cppOpFuncName) + { + return QString("Py_%1").arg(m_pythonOperators.value(cppOpFuncName).toUpper()); + } + static QString pythonRichCompareOperatorId(const AbstractMetaFunction* func) + { + return pythonRichCompareOperatorId(func->originalName()); + } + + static QString cpythonOperatorFunctionName(const AbstractMetaFunction* func); + + static bool isNumber(QString cpythonApiName); + static bool isNumber(const TypeEntry* type); + static bool isNumber(const AbstractMetaType* type); + static bool isPyInt(const TypeEntry* type); + static bool isPyInt(const AbstractMetaType* type); + static bool isReverseOperator(const AbstractMetaFunction* func); + + QString cpythonBaseName(const TypeEntry* type); + QString cpythonBaseName(const AbstractMetaType* type); + QString cpythonTypeName(const TypeEntry* type); + QString cpythonCheckFunction(const TypeEntry* type, bool genericNumberType = false); + QString cpythonCheckFunction(const AbstractMetaType* type, bool genericNumberType = false); + QString cpythonFunctionName(const AbstractMetaFunction* func); + QString cpythonEnumName(const EnumTypeEntry* enumEntry); + QString cpythonEnumName(const AbstractMetaEnum* metaEnum) + { + return cpythonEnumName(metaEnum->typeEntry()); + } + + QString getFunctionReturnType(const AbstractMetaFunction* func) const; + QString getFormatUnitString(const AbstractMetaArgumentList arguments) const; + + virtual bool prepareGeneration(const QMap& args) + { + return true; + } + +protected: + bool m_native_jump_table; + static QHash m_pythonPrimitiveTypeName; + static QHash m_pythonOperators; + static QHash m_formatUnits; + + const char* name() const { return "Shiboken"; } + + /** + * Initialize correspondences between primitive and Python types + */ + static void initPrimitiveTypesCorrespondences(); + + static QString retvalVariableName() { return QString("cpp_result"); } + + static FunctionModificationList functionModifications(const AbstractMetaFunction* func); + AbstractMetaFunctionList queryFunctions(const AbstractMetaClass* metaClass, bool allFunction = false); + void writeFunctionCall(QTextStream& s, + const AbstractMetaFunction* metaFunc, + uint options = 0) const; + + AbstractMetaFunctionList filterFunctions(const AbstractMetaClass* metaClass); + AbstractMetaFunctionList queryGlobalOperators(const AbstractMetaClass* metaClass); + AbstractMetaFunctionList sortContructor(AbstractMetaFunctionList list); +}; + + +#endif // SHIBOKENGENERATOR_H + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..c1569517e --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +add_subdirectory(libsample) +add_subdirectory(samplebinding) + +file(GLOB TEST_FILES samplebinding/*_test.py) + +foreach(test_file ${TEST_FILES}) + string(REGEX MATCH "/([^/]+)_test.py" test_name ${test_file}) + add_test(${CMAKE_MATCH_1} sh + ${CMAKE_CURRENT_SOURCE_DIR}/run_test.sh + "${libsample_BINARY_DIR}:${libshiboken_BINARY_DIR}" + "${sample_BINARY_DIR}" + ${PYTHON_EXECUTABLE} + ${test_file}) + set_tests_properties(${CMAKE_MATCH_1} PROPERTIES TIMEOUT 5) + +# Should set python path here +# Looks like it's fixed in 2.8: +# http://www.vtk.org/Bug/print_bug_page.php?bug_id=7885 +endforeach(test_file ${TEST_FILES}) + diff --git a/tests/libsample/CMakeLists.txt b/tests/libsample/CMakeLists.txt new file mode 100644 index 000000000..a630b3cb4 --- /dev/null +++ b/tests/libsample/CMakeLists.txt @@ -0,0 +1,22 @@ +project(libsample) + +set(libsample_SRC +abstract.cpp +complex.cpp +derived.cpp +functions.cpp +implicitconv.cpp +kindergarten.cpp +listuser.cpp +modifications.cpp +pairuser.cpp +point.cpp +reference.cpp +samplenamespace.cpp +size.cpp +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +add_library(libsample SHARED ${libsample_SRC}) +set_property(TARGET libsample PROPERTY PREFIX "") + diff --git a/tests/libsample/abstract.cpp b/tests/libsample/abstract.cpp new file mode 100644 index 000000000..8b49f37f8 --- /dev/null +++ b/tests/libsample/abstract.cpp @@ -0,0 +1,64 @@ +#include +#include "abstract.h" + +using namespace std; + +Abstract::Abstract(int id) : m_id(id) +{ + cout << __PRETTY_FUNCTION__; + show(); + cout << endl; +} + +Abstract::~Abstract() +{ + cout << __PRETTY_FUNCTION__; + show(); + cout << endl; +} + +void +Abstract::unpureVirtual() +{ + cout << __PRETTY_FUNCTION__ << endl; +} + +void +Abstract::callUnpureVirtual() +{ + cout << __PRETTY_FUNCTION__ << " --- BEGIN" << endl; + this->unpureVirtual(); + cout << __PRETTY_FUNCTION__ << " --- END" << endl; +} + + +void +Abstract::callPureVirtual() +{ + cout << __PRETTY_FUNCTION__ << " --- BEGIN" << endl; + this->pureVirtual(); + cout << __PRETTY_FUNCTION__ << " --- END" << endl; +} + +void +Abstract::show(PrintFormat format) +{ + cout << '<'; + switch(format) { + case Short: + cout << this; + break; + case Verbose: + cout << "class " << className() << " | cptr: " << this; + cout << ", id: " << m_id; + break; + case OnlyId: + cout << "id: " << m_id; + break; + case ClassNameAndId: + cout << className() << " - id: " << m_id; + break; + } + cout << '>'; +} + diff --git a/tests/libsample/abstract.h b/tests/libsample/abstract.h new file mode 100644 index 000000000..19e3a9dbf --- /dev/null +++ b/tests/libsample/abstract.h @@ -0,0 +1,37 @@ +#ifndef ABSTRACT_H +#define ABSTRACT_H + +class Abstract +{ +public: + enum PrintFormat { + Short, + Verbose, + OnlyId, + ClassNameAndId + }; + + Abstract(int id = -1); + virtual ~Abstract(); + + int id() { return m_id; } + + // factory method + static Abstract* createObject() { return 0; } + + virtual void pureVirtual() = 0; + virtual void unpureVirtual(); + + void callPureVirtual(); + void callUnpureVirtual(); + + void show(PrintFormat format = Verbose); + +protected: + virtual const char* className() { return "Abstract"; } + +private: + int m_id; +}; +#endif // ABSTRACT_H + diff --git a/tests/libsample/complex.cpp b/tests/libsample/complex.cpp new file mode 100644 index 000000000..9e93c961c --- /dev/null +++ b/tests/libsample/complex.cpp @@ -0,0 +1,27 @@ +#include +#include "complex.h" + +using namespace std; + +Complex::Complex(double real, double imag) + : m_real(real), m_imag(imag) +{ + // cout << __PRETTY_FUNCTION__ << "[real=0.0, imag=0.0]" << endl; +} + +Complex +Complex::operator+(Complex& other) +{ + Complex result; + result.setReal(m_real + other.real()); + result.setImaginary(m_imag + other.imag()); + return result; +} + +void +Complex::show() +{ + cout << "(real: " << m_real << ", imag: " << m_imag << ")"; +} + + diff --git a/tests/libsample/complex.h b/tests/libsample/complex.h new file mode 100644 index 000000000..f8efb790c --- /dev/null +++ b/tests/libsample/complex.h @@ -0,0 +1,25 @@ +#ifndef COMPLEX_H +#define COMPLEX_H + +class Complex +{ +public: + Complex(double real = 0.0, double imag = 0.0); + ~Complex() {} + + double real() const { return m_real; } + void setReal(double real) { m_real = real; } + double imag() const { return m_imag; } + void setImaginary(double imag) { m_imag = imag; } + + Complex operator+(Complex& other); + + void show(); + +private: + double m_real; + double m_imag; +}; + +#endif // COMPLEX_H + diff --git a/tests/libsample/derived.cpp b/tests/libsample/derived.cpp new file mode 100644 index 000000000..9a86a9c94 --- /dev/null +++ b/tests/libsample/derived.cpp @@ -0,0 +1,80 @@ +#include +#include "derived.h" + +using namespace std; + +Derived::Derived(int id) : Abstract(id) +{ + cout << __PRETTY_FUNCTION__; + show(); + cout << endl; +} + +Derived::~Derived() +{ + cout << __PRETTY_FUNCTION__; + show(); + cout << endl; +} + +Abstract* +Derived::createObject() +{ + static int id = 100; + return new Derived(id++); +} + +void +Derived::pureVirtual() +{ + cout << __PRETTY_FUNCTION__ << endl; +} + +void +Derived::unpureVirtual() +{ + cout << __PRETTY_FUNCTION__ << endl; +} + +bool +Derived::singleArgument(bool b) +{ + cout << __PRETTY_FUNCTION__ << endl; + return !b; +} + +double +Derived::defaultValue(int n) +{ + cout << __PRETTY_FUNCTION__ << "[n = 0]" << endl; + return ((double) n) + 0.1; +} + +PolymorphicFuncEnum +Derived::polymorphic(int i, int d) +{ + cout << __PRETTY_FUNCTION__ << "[i = 0, d = 0]" << endl; + return PolymorphicFunc_ii; +} + +PolymorphicFuncEnum +Derived::polymorphic(double n) +{ + cout << __PRETTY_FUNCTION__ << endl; + return PolymorphicFunc_d; +} + +Derived::OtherPolymorphicFuncEnum +Derived::otherPolymorphic(int a, int b, bool c, double d) +{ + cout << __PRETTY_FUNCTION__ << endl; + return OtherPolymorphicFunc_iibd; +} + +Derived::OtherPolymorphicFuncEnum +Derived::otherPolymorphic(int a, double b) +{ + cout << __PRETTY_FUNCTION__ << endl; + return OtherPolymorphicFunc_id; +} + diff --git a/tests/libsample/derived.h b/tests/libsample/derived.h new file mode 100644 index 000000000..5efc7c4b4 --- /dev/null +++ b/tests/libsample/derived.h @@ -0,0 +1,46 @@ +#ifndef DERIVED_H +#define DERIVED_H + +#include "abstract.h" + +enum PolymorphicFuncEnum { + PolymorphicFunc_ii, + PolymorphicFunc_d +}; + +class Derived : public Abstract +{ +public: + enum OtherPolymorphicFuncEnum { + OtherPolymorphicFunc_iibd, + OtherPolymorphicFunc_id + }; + + Derived(int id = -1); + virtual ~Derived(); + virtual void pureVirtual(); + virtual void unpureVirtual(); + + // factory method + static Abstract* createObject(); + + // single argument + bool singleArgument(bool b); + + // method with default value + double defaultValue(int n = 0); + + // overloads + PolymorphicFuncEnum polymorphic(int i = 0, int d = 0); + PolymorphicFuncEnum polymorphic(double n); + + // more overloads + OtherPolymorphicFuncEnum otherPolymorphic(int a, int b, bool c, double d); + OtherPolymorphicFuncEnum otherPolymorphic(int a, double b); + +protected: + const char* getClassName() { return className(); } + virtual const char* className() { return "Derived"; } +}; +#endif // DERIVED_H + diff --git a/tests/libsample/functions.cpp b/tests/libsample/functions.cpp new file mode 100644 index 000000000..c35bc5be3 --- /dev/null +++ b/tests/libsample/functions.cpp @@ -0,0 +1,85 @@ +#include "functions.h" +#include +#include + +using namespace std; + +void +printSomething() +{ + cout << __PRETTY_FUNCTION__ << endl; +} + +int +gimmeInt() +{ + static int val = 2; + val = val * 1.3; + return val; +} + +double +gimmeDouble() +{ + static double val = 7.77; + val = val * 1.3; + return val; +} + +std::list +gimmeComplexList() +{ + std::list lst; + lst.push_back(Complex()); + lst.push_back(Complex(1.1, 2.2)); + lst.push_back(Complex(1.3, 2.4)); + return lst; +} + +Complex +sumComplexPair(std::pair cpx_pair) +{ + return cpx_pair.first + cpx_pair.second; +} + +double +multiplyPair(std::pair pair) +{ + return pair.first * pair.second; +} + +int +countCharacters(const char* text) +{ + int count; + for(count = 0; text[count] != '\0'; count++) + ; + return count; +} + +char* +makeCString() +{ + char* string = new char[strlen(__FUNCTION__) + 1]; + strcpy(string, __FUNCTION__); + return string; +} + +const char* +returnCString() +{ + return __PRETTY_FUNCTION__; +} + +GlobalPolyFuncEnum +polymorphicFunc(int val) +{ + return GlobalPolyFunc_i; +} + +GlobalPolyFuncEnum +polymorphicFunc(double val) +{ + return GlobalPolyFunc_d; +} + diff --git a/tests/libsample/functions.h b/tests/libsample/functions.h new file mode 100644 index 000000000..77b2cb68f --- /dev/null +++ b/tests/libsample/functions.h @@ -0,0 +1,36 @@ +#ifndef FUNCTIONS_H +#define FUNCTIONS_H + +#include +#include +#include "complex.h" + +enum GlobalEnum { + NoThing, + FirstThing, + SecondThing, + ThirdThing +}; + +enum GlobalPolyFuncEnum { + GlobalPolyFunc_i, + GlobalPolyFunc_d +}; + +void printSomething(); +int gimmeInt(); +double gimmeDouble(); +double multiplyPair(std::pair pair); +std::list gimmeComplexList(); +Complex sumComplexPair(std::pair cpx_pair); + +int countCharacters(const char* text); +char* makeCString(); +const char* returnCString(); + +// Tests polymorphism on functions (!methods) +GlobalPolyFuncEnum polymorphicFunc(int val); +GlobalPolyFuncEnum polymorphicFunc(double val); + +#endif // FUNCTIONS_H + diff --git a/tests/libsample/implicitconv.cpp b/tests/libsample/implicitconv.cpp new file mode 100644 index 000000000..902707e18 --- /dev/null +++ b/tests/libsample/implicitconv.cpp @@ -0,0 +1,38 @@ +#include "implicitconv.h" + +ImplicitConv +ImplicitConv::implicitConvCommon(ImplicitConv implicit) +{ + return implicit; +} + +ImplicitConv +ImplicitConv::implicitConvDefault(ImplicitConv implicit) +{ + return implicit; +} + +ImplicitConv::ICPolymorphicFuncEnum +ImplicitConv::implicitConvPolymorphism(ImplicitConv implicit, int dummyArg) +{ + return ImplicitConv::PolyFunc_Ii; +} + +ImplicitConv::ICPolymorphicFuncEnum +ImplicitConv::implicitConvPolymorphism(ImplicitConv implicit, bool dummyArg) +{ + return ImplicitConv::PolyFunc_Ib; +} + +ImplicitConv::ICPolymorphicFuncEnum +ImplicitConv::implicitConvPolymorphism(int dummyArg) +{ + return ImplicitConv::PolyFunc_i; +} + +ImplicitConv::ICPolymorphicFuncEnum +ImplicitConv::implicitConvPolymorphism(CtorEnum dummyArg) +{ + return ImplicitConv::PolyFunc_C; +} + diff --git a/tests/libsample/implicitconv.h b/tests/libsample/implicitconv.h new file mode 100644 index 000000000..538150870 --- /dev/null +++ b/tests/libsample/implicitconv.h @@ -0,0 +1,44 @@ +#ifndef IMPLICITCONV_H +#define IMPLICITCONV_H + +class ImplicitConv +{ +public: + enum CtorEnum { + CtorNone, + CtorOne, + CtorTwo, + CtorThree + }; + + enum ICPolymorphicFuncEnum { + PolyFunc_Ii, + PolyFunc_Ib, + PolyFunc_i, + PolyFunc_C + }; + + ImplicitConv() : m_ctorEnum(CtorNone), m_objId(-1) {} + ImplicitConv(int objId) : m_ctorEnum(CtorOne), m_objId(objId) {} + ImplicitConv(CtorEnum ctorEnum) : m_ctorEnum(ctorEnum), m_objId(-1) {} + ~ImplicitConv() {} + + CtorEnum ctorEnum() { return m_ctorEnum; } + int objId() { return m_objId; } + + static ImplicitConv implicitConvCommon(ImplicitConv implicit); + + static ImplicitConv implicitConvDefault(ImplicitConv implicit = CtorTwo); + + static ICPolymorphicFuncEnum implicitConvPolymorphism(ImplicitConv implicit, int dummyArg); + static ICPolymorphicFuncEnum implicitConvPolymorphism(ImplicitConv implicit, bool dummyArg); + static ICPolymorphicFuncEnum implicitConvPolymorphism(int dummyArg); + static ICPolymorphicFuncEnum implicitConvPolymorphism(CtorEnum dummyArg); + +private: + CtorEnum m_ctorEnum; + int m_objId; +}; + +#endif // IMPLICITCONV_H + diff --git a/tests/libsample/kindergarten.cpp b/tests/libsample/kindergarten.cpp new file mode 100644 index 000000000..8332e4fe7 --- /dev/null +++ b/tests/libsample/kindergarten.cpp @@ -0,0 +1,64 @@ +#include +#include "kindergarten.h" + +using namespace std; + +KinderGarten::~KinderGarten() +{ + cout << __PRETTY_FUNCTION__ << " ---- BEGIN" << endl; + killChildren(); + cout << __PRETTY_FUNCTION__ << " ---- END" << endl; +} + +void +KinderGarten::addChild(Abstract* child) +{ + m_children.push_back(child); +} + +void +KinderGarten::killChildren() +{ + cout << __PRETTY_FUNCTION__ << endl; + while (!m_children.empty()) { + m_children.back()->show(); + cout << endl; + delete m_children.back(); + m_children.pop_back(); + } +} + +void +KinderGarten::killChild(Abstract* child) +{ + cout << __PRETTY_FUNCTION__ << endl; + if (child) { + m_children.remove(child); +// delete child; + } +} + +Abstract* +KinderGarten::releaseChild(Abstract* child) +{ + for(ChildList::iterator child_iter = m_children.begin(); + child_iter != m_children.end(); child_iter++) { + if (child == *child_iter) { + m_children.erase(child_iter); + return child; + } + } +} + +void +KinderGarten::show() +{ + cout << "["; + for(ChildList::iterator child_iter = m_children.begin(); + child_iter != m_children.end(); child_iter++) { + if (child_iter != m_children.begin()) + cout << ", "; + (*child_iter)->show(); + } + cout << "]"; +} diff --git a/tests/libsample/kindergarten.h b/tests/libsample/kindergarten.h new file mode 100644 index 000000000..b0a083dce --- /dev/null +++ b/tests/libsample/kindergarten.h @@ -0,0 +1,29 @@ +#ifndef KINDERGARTEN_H +#define KINDERGARTEN_H + +#include +#include "abstract.h" + +class KinderGarten +{ +public: + typedef std::list ChildList; + + KinderGarten() {} + ~KinderGarten(); + + void addChild(Abstract* child); + Abstract* releaseChild(Abstract* child); + ChildList children() { return m_children; } + + void killChildren(); + void killChild(Abstract* child); + + void show(); + +private: + ChildList m_children; +}; + +#endif // KINDERGARTEN_H + diff --git a/tests/libsample/listuser.cpp b/tests/libsample/listuser.cpp new file mode 100644 index 000000000..0b9a14ff5 --- /dev/null +++ b/tests/libsample/listuser.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "listuser.h" + +using namespace std; + +std::list +ListUser::callCreateList() +{ + cout << __PRETTY_FUNCTION__ << endl; + return createList(); +} + +std::list +ListUser::createList() +{ + cout << __PRETTY_FUNCTION__ << endl; + std::list retval; + for (int i = 0; i < 4; i++) + retval.push_front(rand()); + return retval; +} + +std::list +ListUser::createComplexList(Complex cpx0, Complex cpx1) +{ + cout << __PRETTY_FUNCTION__ << endl; + std::list retval; + retval.push_back(cpx0); + retval.push_back(cpx1); + return retval; +} + +double +ListUser::sumList(std::list vallist) +{ + return std::accumulate(vallist.begin(), vallist.end(), 0.0); +} + +double +ListUser::sumList(std::list vallist) +{ + return std::accumulate(vallist.begin(), vallist.end(), 0.0); +} + + diff --git a/tests/libsample/listuser.h b/tests/libsample/listuser.h new file mode 100644 index 000000000..c1ea7091e --- /dev/null +++ b/tests/libsample/listuser.h @@ -0,0 +1,23 @@ +#ifndef LISTUSER_H +#define LISTUSER_H + +#include +#include "complex.h" + +class ListUser +{ +public: + ListUser() {} + ~ListUser() {} + + virtual std::list createList(); + std::list callCreateList(); + + static std::list createComplexList(Complex cpx0, Complex cpx1); + + double sumList(std::list vallist); + double sumList(std::list vallist); +}; + +#endif // LISTUSER_H + diff --git a/tests/libsample/main.cpp b/tests/libsample/main.cpp new file mode 100644 index 000000000..7fc563679 --- /dev/null +++ b/tests/libsample/main.cpp @@ -0,0 +1,216 @@ +#include +#include +#include "abstract.h" +#include "derived.h" +#include "kindergarten.h" +#include "complex.h" +#include "point.h" +#include "size.h" +#include "listuser.h" +#include "samplenamespace.h" + +using namespace std; + +int +main(int argv, char **argc) +{ + cout << endl; + + Derived derived; + + cout << endl; + + derived.unpureVirtual(); + derived.pureVirtual(); + derived.callPureVirtual(); + + cout << endl; + Abstract* abs; + abs = Abstract::createObject(); + cout << "Abstract::createObject(): " << abs << endl << endl; + delete abs; + + abs = Derived::createObject(); + cout << "Derived::createObject() : "; + abs->show(); + cout << endl; + delete abs; + cout << endl; + + abs = Derived::createObject(); + cout << "Derived::createObject() : "; + abs->show(); + cout << endl; + delete abs; + cout << endl; + + cout << endl << "-----------------------------------------" << endl; + + KinderGarten kg; + Derived* d[] = { 0, 0, 0 }; + + for (int i = 0; i < 3; i++) { + d[i] = new Derived(i); + d[i]->show(); + cout << endl; + kg.addChild(d[i]); + } + + kg.show(); + cout << endl; + + cout << endl << "* kill child "; + d[2]->show(); + cout << " ----------------" << endl; + kg.killChild(d[2]); + kg.show(); + cout << endl; + + cout << endl << "* release child "; + d[1]->show(); + cout << " -------------" << endl; + Abstract* released = kg.releaseChild(d[1]); + cout << "released: "; + released->show(); + cout << endl; + kg.show(); + cout << endl; + + cout << endl << "* kill children ------------------------------------" << endl; + kg.killChildren(); + kg.show(); + cout << endl << endl; + + cout << "-----------------------------------------" << endl; + ListUser lu; + cout << "ListUser::createList()" << endl; + std::list intlist = lu.createList(); + for (std::list::iterator it = intlist.begin(); it != intlist.end(); it++) { + cout << "* " << *it << endl; + } + + cout << "ListUser::createComplexList" << endl; + std::list cpxlist = ListUser::createComplexList(Complex(1.1, 2.2), Complex(3.3, 4.4)); + for (std::list::iterator it = cpxlist.begin(); it != cpxlist.end(); it++) { + cout << "* "; + (*it).show(); + cout << endl; + } + cout << endl; + + cout << "-----------------------------------------" << endl; + cout << "SampleNamespace" << endl; + + cout << "SampleNamespace::RandomNumber: "; + cout << SampleNamespace::getNumber(SampleNamespace::RandomNumber); + cout << endl; + cout << "SampleNamespace::UnixTime: "; + cout << SampleNamespace::getNumber(SampleNamespace::UnixTime); + cout << endl; + double val_d = 1.3; + cout << "SampleNamespace::powerOfTwo(" << val_d << "): "; + cout << SampleNamespace::powerOfTwo(val_d) << endl; + int val_i = 7; + cout << "SampleNamespace::powerOfTwo(" << val_i << "): "; + cout << SampleNamespace::powerOfTwo(val_i) << endl; + cout << endl; + + cout << "-----------------------------------------" << endl; + cout << "Point" << endl; + + Point p1(1.1, 2.2); + cout << "p1: "; + p1.show(); + cout << endl; + + Point p2(3.4, 5.6); + cout << "p2: "; + p2.show(); + cout << endl; + + cout << "p1 + p2 == "; + (p1 + p2).show(); + cout << endl; + + cout << "p1 * 2.0 == "; + (p1 * 2.0).show(); + cout << endl; + + cout << "1.5 * p2 == "; + (1.5 * p2).show(); + cout << endl; + + cout << "p1: "; + p1.show(); + cout << endl << "p2: "; + p2.show(); + cout << endl << "p1 += p2" << endl; + p1 += p2; + cout << "p1: "; + p1.show(); + cout << endl; + + cout << "p1 == p2 ? " << ((p1 == p2) ? "true" : "false") << endl; + cout << "p1 == p1 ? " << ((p1 == p1) ? "true" : "false") << endl; + cout << "p2 == p2 ? " << ((p2 == p2) ? "true" : "false") << endl; + + cout << "-----------------------------------------" << endl; + cout << "Size" << endl; + + Size s1(2, 2); + cout << "s1: "; + s1.show(); + cout << ", area: " << s1.calculateArea(); + cout << endl; + + Size s2(3, 5); + cout << "s2: "; + s2.show(); + cout << ", area: " << s2.calculateArea(); + cout << endl; + + cout << endl; + + cout << "s1 == s2 ? " << ((s1 == s2) ? "true" : "false") << endl; + cout << "s1 != s2 ? " << ((s1 != s2) ? "true" : "false") << endl; + + cout << "s1 < s2 ? " << ((s1 < s2) ? "true" : "false") << endl; + cout << "s1 <= s2 ? " << ((s1 <= s2) ? "true" : "false") << endl; + cout << "s1 > s2 ? " << ((s1 > s2) ? "true" : "false") << endl; + cout << "s1 >= s2 ? " << ((s1 >= s2) ? "true" : "false") << endl; + + cout << "s1 < 10 ? " << ((s1 < 10) ? "true" : "false") << endl; + cout << "s1 <= 10 ? " << ((s1 <= 10) ? "true" : "false") << endl; + cout << "s1 > 10 ? " << ((s1 > 10) ? "true" : "false") << endl; + cout << "s1 >= 10 ? " << ((s1 >= 10) ? "true" : "false") << endl; + cout << "s2 < 10 ? " << ((s2 < 10) ? "true" : "false") << endl; + cout << "s2 <= 10 ? " << ((s2 <= 10) ? "true" : "false") << endl; + cout << "s2 > 10 ? " << ((s2 > 10) ? "true" : "false") << endl; + cout << "s2 >= 10 ? " << ((s2 >= 10) ? "true" : "false") << endl; + cout << endl; + + cout << "s1: "; + s1.show(); + cout << endl << "s2: "; + s2.show(); + cout << endl << "s1 += s2" << endl; + s1 += s2; + cout << "s1: "; + s1.show(); + cout << endl; + + cout << endl; + + cout << "s1: "; + s1.show(); + cout << endl << "s1 *= 2.0" << endl; + s1 *= 2.0; + cout << "s1: "; + s1.show(); + cout << endl; + + cout << endl; + + return 0; +} + diff --git a/tests/libsample/modifications.cpp b/tests/libsample/modifications.cpp new file mode 100644 index 000000000..68cf6fe6c --- /dev/null +++ b/tests/libsample/modifications.cpp @@ -0,0 +1,68 @@ +#include +#include "modifications.h" + +using namespace std; + +std::pair +Modifications::pointToPair(Point pt, bool* ok) +{ + std::pair retval(pt.x(), pt.y()); + *ok = true; + return retval; +} + +double +Modifications::multiplyPointCoordsPlusValue(bool* ok, Point pt, double value) +{ + double retval = (pt.x() * pt.y()) + value; + *ok = true; + return retval; +} + +int +Modifications::doublePlus(int value, int plus) +{ + return (2 * value) + plus; +} + +int +Modifications::power(int base, int exponent) +{ + if (exponent == 0) + return 1; + int retval = base; + for (int i = 1; i < exponent; i++) + retval = retval * base; + return retval; +} + +int +Modifications::timesTen(int number) +{ + return number * 10; +} + +int +Modifications::increment(int number) +{ + return ++number; +} + +void +Modifications::exclusiveCppStuff() +{ + cout << __PRETTY_FUNCTION__ << endl; +} + +int +Modifications::cppMultiply(int a, int b) +{ + return a * b; +} + +const char* +Modifications::className() +{ + return "Modifications"; +} + diff --git a/tests/libsample/modifications.h b/tests/libsample/modifications.h new file mode 100644 index 000000000..793808161 --- /dev/null +++ b/tests/libsample/modifications.h @@ -0,0 +1,73 @@ +#ifndef MODIFICATIONS_H +#define MODIFICATIONS_H + +#include +#include "point.h" + +class Modifications +{ +public: + Modifications() {} + ~Modifications() {} + + enum PolymorphicModFunc { + PolymorphicNone, + Polymorphic_ibid, + Polymorphic_ibib, + Polymorphic_ibiP, + Polymorphic_ibii, + Polymorphic_ibPP + }; + + // those polymorphic methods should be heavily modified + // to push the overload decisor to its limits + PolymorphicModFunc polymorphic(int a0, bool b0, int c0, double d0) { return Polymorphic_ibid; } + PolymorphicModFunc polymorphic(int a1, bool b1, int c1, bool d1) { return Polymorphic_ibib; } + PolymorphicModFunc polymorphic(int a2, bool b2, int c2, Point d2) { return Polymorphic_ibiP; } + PolymorphicModFunc polymorphic(int a3, bool b3, int c3 = 123, int d3 = 456) { return Polymorphic_ibii; } + PolymorphicModFunc polymorphic(int a4, bool b4, Point c4, Point d4) { return Polymorphic_ibPP; } + + // 'ok' must be removed and the return value will be changed + // to a tuple (PyObject*) containing the expected result plus + // the 'ok' value as a Python boolean + std::pair pointToPair(Point pt, bool* ok); + + // same as 'pointToPair' except that this time 'ok' is the first argument + double multiplyPointCoordsPlusValue(bool* ok, Point pt, double value); + + // completely remove 'plus' from the Python side + int doublePlus(int value, int plus = 0); + + // the default value for both arguments must be changed in Python + int power(int base = 1, int exponent = 0); + + // in Python set argument default value to 10 + int timesTen(int number); + + // in Python remove the argument default value + int increment(int number = 0); + + // don't export this method to Python + void exclusiveCppStuff(); + + // change the name of this regular method + int cppMultiply(int a, int b); + + // change the name of this virtual method + virtual const char* className(); +}; + +class AbstractModifications : public Modifications +{ +public: + AbstractModifications() {} + ~AbstractModifications() {} + + bool invert(bool value) { return !value; } + + // completely remove this method in Python + virtual void pointlessPureVirtualMethod() = 0; +}; + +#endif // MODIFICATIONS_H + diff --git a/tests/libsample/pairuser.cpp b/tests/libsample/pairuser.cpp new file mode 100644 index 000000000..8983630e2 --- /dev/null +++ b/tests/libsample/pairuser.cpp @@ -0,0 +1,32 @@ +#include +#include "pairuser.h" + +using namespace std; + +std::pair +PairUser::callCreatePair() +{ + cout << __PRETTY_FUNCTION__ << endl; + return createPair(); +} + +std::pair +PairUser::createPair() +{ + cout << __PRETTY_FUNCTION__ << endl; + return std::pair(10, 20); +} + +std::pair +PairUser::createComplexPair(Complex cpx0, Complex cpx1) +{ + cout << __PRETTY_FUNCTION__ << endl; + return std::pair(cpx0, cpx1); +} + +double +PairUser::sumPair(std::pair pair) +{ + return ((double) pair.first) + pair.second; +} + diff --git a/tests/libsample/pairuser.h b/tests/libsample/pairuser.h new file mode 100644 index 000000000..27cf76681 --- /dev/null +++ b/tests/libsample/pairuser.h @@ -0,0 +1,19 @@ +#ifndef PAIRUSER_H +#define PAIRUSER_H + +#include +#include "complex.h" + +class PairUser +{ +public: + PairUser() {} + ~PairUser() {} + + virtual std::pair createPair(); + std::pair callCreatePair(); + static std::pair createComplexPair(Complex cpx0, Complex cpx1); + double sumPair(std::pair pair); +}; +#endif // PAIRUSER_H + diff --git a/tests/libsample/point.cpp b/tests/libsample/point.cpp new file mode 100644 index 000000000..ad816025e --- /dev/null +++ b/tests/libsample/point.cpp @@ -0,0 +1,111 @@ +#include +#include "point.h" + +using namespace std; + +Point::Point(int x, int y) : m_x(x), m_y(y) +{ + // cout << __PRETTY_FUNCTION__ << " [x=0, y=0]" << endl; +} + +Point::Point(double x, double y) : m_x(x), m_y(y) +{ + // cout << __PRETTY_FUNCTION__ << endl; +} + +void +Point::show() +{ + cout << "(x: " << m_x << ", y: " << m_y << ")"; +} + +bool +Point::operator==(const Point& other) +{ + return m_x == other.m_x && m_y == other.m_y; +} + +Point +Point::operator+(const Point& other) +{ + return Point(m_x + other.m_x, m_y + other.m_y); +} + +Point +Point::operator-(const Point& other) +{ + return Point(m_x - other.m_x, m_y - other.m_y); +} + +Point& +Point::operator+=(Point &other) +{ + m_x += other.m_x; + m_y += other.m_y; + return *this; +} + +Point& +Point::operator-=(Point &other) +{ + m_x -= other.m_x; + m_y -= other.m_y; + return *this; +} + +Point +operator*(Point& pt, double mult) +{ + return Point(pt.m_x * mult, pt.m_y * mult); +} + +Point +operator*(Point& pt, int mult) +{ + return Point(((int) pt.m_x) * mult, ((int) pt.m_y) * mult); +} + +Point +operator*(double mult, Point& pt) +{ + return Point(pt.m_x * mult, pt.m_y * mult); +} + +Point +operator*(int mult, Point& pt) +{ + return Point(((int) pt.m_x) * mult, ((int) pt.m_y) * mult); +} + +Point +operator-(const Point& pt) +{ + return Point(-pt.m_x, -pt.m_y); +} + +bool +operator!(const Point& pt) +{ + return (pt.m_x == 0.0 && pt.m_y == 0.0); +} + +Complex +transmutePointIntoComplex(Point point) +{ + Complex cpx(point.x(), point.y()); + // cout << __PRETTY_FUNCTION__ << " "; + // point.show(); + // cout << endl; + return cpx; +} + +Point +transmuteComplexIntoPoint(Complex cpx) +{ + Point pt(cpx.real(), cpx.imag()); + // cout << __PRETTY_FUNCTION__ << " "; + // cpx.show(); + // cout << endl; + return pt; +} + diff --git a/tests/libsample/point.h b/tests/libsample/point.h new file mode 100644 index 000000000..e5ec9c1bc --- /dev/null +++ b/tests/libsample/point.h @@ -0,0 +1,51 @@ +#ifndef POINT_H +#define POINT_H + +#include "complex.h" +#include + +class Point +{ +public: + Point(int x = 0, int y = 0); + Point(double x, double y); + ~Point() {} + + double x() const { return m_x; } + double y() const { return m_y; } + + bool operator==(const Point& other); + Point operator+(const Point& other); + Point operator-(const Point& other); + + friend Point operator*(Point& pt, double mult); + friend Point operator*(Point& pt, int mult); + friend Point operator*(double mult, Point& pt); + friend Point operator*(int mult, Point& pt); + friend Point operator-(const Point& pt); + friend bool operator!(const Point& pt); + + Point& operator+=(Point &other); + Point& operator-=(Point &other); + + void show(); + +private: + double m_x; + double m_y; +}; + +Point operator*(Point& pt, double mult); +Point operator*(Point& pt, int mult); +Point operator*(double mult, Point& pt); +Point operator*(int mult, Point& pt); +Point operator-(const Point& pt); +bool operator!(const Point& pt); + +Complex transmutePointIntoComplex(Point point); +Point transmuteComplexIntoPoint(Complex cpx); + +Point operator*(Point& pt, double multiplier); + +#endif // POINT_H + diff --git a/tests/libsample/reference.cpp b/tests/libsample/reference.cpp new file mode 100644 index 000000000..fb3e78e49 --- /dev/null +++ b/tests/libsample/reference.cpp @@ -0,0 +1,11 @@ +#include +#include "reference.h" + +using namespace std; + +void +Reference::show() const +{ + cout << "Reference.objId: " << m_objId; +} + diff --git a/tests/libsample/reference.h b/tests/libsample/reference.h new file mode 100644 index 000000000..bafd2492a --- /dev/null +++ b/tests/libsample/reference.h @@ -0,0 +1,24 @@ +#ifndef REFERENCE_H +#define REFERENCE_H + +class Reference +{ +public: + explicit Reference(int objId = -1) + : m_objId(objId) {} + ~Reference() {} + + double objId() { return m_objId; } + void setObjId(int objId) { m_objId = objId; } + + static int usesReference(Reference& r) { return r.m_objId; } + static int usesConstReference(const Reference& r) { return r.m_objId; } + + void show() const; + +private: + int m_objId; +}; + +#endif // REFERENCE_H + diff --git a/tests/libsample/samplenamespace.cpp b/tests/libsample/samplenamespace.cpp new file mode 100644 index 000000000..ebf2ff74d --- /dev/null +++ b/tests/libsample/samplenamespace.cpp @@ -0,0 +1,48 @@ +#include +#include +#include +#include "samplenamespace.h" + +using namespace std; + +namespace SampleNamespace +{ + +OutValue +enumInEnumOut(InValue in) +{ + OutValue retval; + switch(in) { + case ZeroIn: + retval = ZeroOut; + break; + case OneIn: + retval = OneOut; + break; + case TwoIn: + retval = TwoOut; + break; + default: + retval = (OutValue) -1; + } + return retval; +} + +int +getNumber(Option opt) +{ + int retval; + switch(opt) { + case RandomNumber: + retval = rand() % 100; + break; + case UnixTime: + retval = (int) time(0); + break; + default: + retval = 0; + } + return retval; +} + +} // namespace SampleNamespace diff --git a/tests/libsample/samplenamespace.h b/tests/libsample/samplenamespace.h new file mode 100644 index 000000000..2c65aeab0 --- /dev/null +++ b/tests/libsample/samplenamespace.h @@ -0,0 +1,36 @@ +#ifndef SAMPLENAMESPACE_H +#define SAMPLENAMESPACE_H + +namespace SampleNamespace +{ + +enum Option { + None, + RandomNumber, + UnixTime +}; + +enum InValue { + ZeroIn, + OneIn, + TwoIn +}; + +enum OutValue { + ZeroOut, + OneOut, + TwoOut +}; + +OutValue enumInEnumOut(InValue in); + +int getNumber(Option opt); + +inline double powerOfTwo(double num) { + return num * num; +} + +} // namespace SampleNamespace + +#endif // SAMPLENAMESPACE_H + diff --git a/tests/libsample/size.cpp b/tests/libsample/size.cpp new file mode 100644 index 000000000..63af9c828 --- /dev/null +++ b/tests/libsample/size.cpp @@ -0,0 +1,11 @@ +#include +#include "size.h" + +using namespace std; + +void +Size::show() const +{ + cout << "(width: " << m_width << ", height: " << m_height << ")"; +} + diff --git a/tests/libsample/size.h b/tests/libsample/size.h new file mode 100644 index 000000000..48f0d772a --- /dev/null +++ b/tests/libsample/size.h @@ -0,0 +1,152 @@ +#ifndef SIZE_H +#define SIZE_H + +class Size +{ +public: + Size(double width = 0.0, double height = 0.0) : m_width(width), m_height(height) {} + ~Size() {} + + double width() { return m_width; } + void setWidth(double width) { m_width = width; } + double height() { return m_height; } + void setHeight(double height) { m_height = height; } + + double calculateArea() const { return m_width * m_height; } + + // Comparison Operators + inline bool operator==(const Size& other) + { + return m_width == other.m_width && m_height == other.m_height; + } + + inline bool operator<(const Size& other) + { + return calculateArea() < other.calculateArea(); + } + + inline bool operator>(const Size& other) + { + return calculateArea() > other.calculateArea(); + } + + inline bool operator<=(const Size& other) + { + return calculateArea() <= other.calculateArea(); + } + + inline bool operator>=(const Size& other) + { + return calculateArea() >= other.calculateArea(); + } + + inline bool operator<(double area) { return calculateArea() < area; } + inline bool operator>(double area) { return calculateArea() > area; } + inline bool operator<=(double area) { return calculateArea() <= area; } + inline bool operator>=(double area) { return calculateArea() >= area; } + + // Arithmetic Operators + Size& operator+=(const Size& s) + { + m_width += s.m_width; + m_height += s.m_height; + return *this; + } + + Size& operator-=(const Size& s) + { + m_width -= s.m_width; + m_height -= s.m_height; + return *this; + } + + Size& operator*=(double mult) + { + m_width *= mult; + m_height *= mult; + return *this; + } + + Size& operator/=(double div) + { + m_width /= div; + m_height /= div; + return *this; + } + + // TODO: add ++size, size++, --size, size-- + + // External operators + friend inline bool operator!=(const Size&, const Size&); + friend inline const Size operator+(const Size&, const Size&); + friend inline const Size operator-(const Size&, const Size&); + friend inline const Size operator*(const Size&, double); + friend inline const Size operator*(double, const Size&); + friend inline const Size operator/(const Size&, double); + + friend inline bool operator<(double, const Size&); + friend inline bool operator>(double, const Size&); + friend inline bool operator<=(double, const Size&); + friend inline bool operator>=(double, const Size&); + + void show() const; + +private: + double m_width; + double m_height; +}; + +// Comparison Operators +inline bool operator!=(const Size& s1, const Size& s2) +{ + return s1.m_width != s2.m_width || s1.m_height != s2.m_height; +} + +inline bool operator<(double area, const Size& s) +{ + return area < s.calculateArea(); +} + +inline bool operator>(double area, const Size& s) +{ + return area > s.calculateArea(); +} + +inline bool operator<=(double area, const Size& s) +{ + return area <= s.calculateArea(); +} + +inline bool operator>=(double area, const Size& s) +{ + return area >= s.calculateArea(); +} + +// Arithmetic Operators +inline const Size operator+(const Size& s1, const Size& s2) +{ + return Size(s1.m_width + s2.m_width, s1.m_height + s2.m_height); +} + +inline const Size operator-(const Size& s1, const Size& s2) +{ + return Size(s1.m_width - s2.m_width, s1.m_height - s2.m_height); +} + +inline const Size operator*(const Size& s, double mult) +{ + return Size(s.m_width * mult, s.m_height * mult); +} + +inline const Size operator*(double mult, const Size& s) +{ + return Size(s.m_width * mult, s.m_height * mult); +} + +inline const Size operator/(const Size& s, double div) +{ + return Size(s.m_width / div, s.m_height / div); +} + +#endif // SIZE_H + diff --git a/tests/run_test.sh b/tests/run_test.sh new file mode 100755 index 000000000..ed86ea30c --- /dev/null +++ b/tests/run_test.sh @@ -0,0 +1,13 @@ +#!/usr/bin/python + +# This is a nasty workaround of a CTest limitation +# of setting the environment variables for the test. + + +LIB_PATH=$LD_LIBRARY_PATH:$1 +PYTHON_PATH=$PYTHONPATH:$2 +PYTHON_EXEC=$3 +TEST_FILE=$4 + +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LIB_PATH PYTHONPATH=$PYTHON_PATH $PYTHON_EXEC $TEST_FILE + diff --git a/tests/samplebinding/CMakeLists.txt b/tests/samplebinding/CMakeLists.txt new file mode 100644 index 000000000..e89d7d765 --- /dev/null +++ b/tests/samplebinding/CMakeLists.txt @@ -0,0 +1,44 @@ +project(sample) + +set(sample_TYPESYSTEM +${CMAKE_CURRENT_SOURCE_DIR}/typesystem_sample.xml +) + +set(sample_SRC +${CMAKE_CURRENT_BINARY_DIR}/sample/abstractmodifications_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/abstract_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/derived_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/implicitconv_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/listuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/modifications_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/pairuser_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/point_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/reference_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/sample_module_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/samplenamespace_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/size_wrapper.cpp +) + +add_custom_command(OUTPUT ${sample_SRC} +COMMAND ${CMAKE_BINARY_DIR}/shiboken + ${CMAKE_CURRENT_SOURCE_DIR}/global.h + --include-paths=${libsample_SOURCE_DIR} + --typesystem-paths=${CMAKE_CURRENT_SOURCE_DIR} + --output-directory=${CMAKE_CURRENT_BINARY_DIR} + ${sample_TYPESYSTEM} +WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +COMMENT "Running generator for test binding..." +) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR} + ${PYTHON_INCLUDE_PATH} + ${libsample_SOURCE_DIR} + ${libshiboken_SOURCE_DIR}) +add_library(sample MODULE ${sample_SRC}) +set_property(TARGET sample PROPERTY PREFIX "") +target_link_libraries(sample + libsample + ${PYTHON_LIBRARIES} + libshiboken) + diff --git a/tests/samplebinding/abstract_test.py b/tests/samplebinding/abstract_test.py new file mode 100755 index 000000000..9d4c51f2c --- /dev/null +++ b/tests/samplebinding/abstract_test.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +'''Test cases for Abstract class''' + +import sys +import unittest + +from sample import Abstract + +class Incomplete(Abstract): + def __init__(self): + Abstract.__init__(self) + +class Concrete(Abstract): + def __init__(self): + Abstract.__init__(self) + self.pure_virtual_called = False + self.unpure_virtual_called = False + + def pureVirtual(self): + self.pure_virtual_called = True + + def unpureVirtual(self): + self.unpure_virtual_called = True + + +class AbstractTest(unittest.TestCase): + '''Test case for Abstract class''' + + def testAbstractPureVirtualMethodAvailability(self): + '''Test if Abstract class pure virtual method was properly wrapped.''' + self.assert_('pureVirtual' in dir(Abstract)) + + def testAbstractInstanciation(self): + '''Test if instanciation of an abstract class raises the correct exception.''' + self.assertRaises(NotImplementedError, Abstract) + + def testUnimplementedPureVirtualMethodCall(self): + '''Test if calling a pure virtual method raises the correct exception.''' + i = Incomplete() + self.assertRaises(NotImplementedError, i.pureVirtual) + + def testReimplementedVirtualMethodCall(self): + '''Test if instanciation of an abstract class raises the correct exception.''' + i = Concrete() + self.assertRaises(NotImplementedError, i.callPureVirtual) + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a virtual method is correctly called from C++.''' + c = Concrete() + c.callUnpureVirtual() + self.assert_(c.unpure_virtual_called) + + def testImplementedPureVirtualMethodCall(self): + '''Test if a Python override of a pure virtual method is correctly called from C++.''' + c = Concrete() + c.callPureVirtual() + self.assert_(c.pure_virtual_called) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/complex_conversions.h b/tests/samplebinding/complex_conversions.h new file mode 100644 index 000000000..8caaef03b --- /dev/null +++ b/tests/samplebinding/complex_conversions.h @@ -0,0 +1,23 @@ +template<> +struct Converter +{ + static PyObject* toPython(ValueHolder cpx) + { + /* + fprintf(stderr, "[%s:%d] cpx.real: %f, cpx.imag: %f\n", + __PRETTY_FUNCTION__, __LINE__, cpx.value.real(), cpx.value.imag()); + PyObject* result = PyComplex_FromDoubles(cpx.value.real(), cpx.value.imag()); + fprintf(stderr, "[%s:%d]", __PRETTY_FUNCTION__, __LINE__); + PyObject_Print(result, stderr, 0); + fprintf(stderr, "\n"); + return result; + */ + return PyComplex_FromDoubles(cpx.value.real(), cpx.value.imag()); + } + static Complex toCpp(PyObject* pyobj) + { + double real = PyComplex_RealAsDouble(pyobj); + double imag = PyComplex_ImagAsDouble(pyobj); + return Complex(real, imag); + } +}; diff --git a/tests/samplebinding/complex_test.py b/tests/samplebinding/complex_test.py new file mode 100755 index 000000000..92da61090 --- /dev/null +++ b/tests/samplebinding/complex_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +'''Test cases for Complex class''' + +import sys +import unittest + +import sample +from sample import Point + +class ComplexTest(unittest.TestCase): + '''Test case for conversions between C++ Complex class to Python complex class''' + + def testFunctionReturningComplexObject(self): + '''Test function returning a C++ Complex object.''' + cpx = sample.transmutePointIntoComplex(Point(5.0, 2.3)) + self.assertEqual(cpx, complex(5.0, 2.3)) + + def testFunctionReceivingComplexObjectAsArgument(self): + '''Test function returning a C++ Complex object.''' + pt = sample.transmuteComplexIntoPoint(complex(1.2, 3.4)) + # these assertions intentionally avoids to test the == operator, + # it should have its own test cases. + self.assertEqual(pt.x(), 1.2) + self.assertEqual(pt.y(), 3.4) + + def testComplexList(self): + '''Test list of C++ Complex objects conversion to a list of Python complex objects.''' + # the global function gimmeComplexList() is expected to return a list + # containing the following Complex values: [0j, 1.1+2.2j, 1.3+2.4j] + cpxlist = sample.gimmeComplexList() + self.assertEqual(cpxlist, [complex(), complex(1.1, 2.2), complex(1.3, 2.4)]) + + def testSumComplexPair(self): + '''Test sum of a tuple containing two complex objects.''' + cpx1 = complex(1.2, 3.4) + cpx2 = complex(5.6, 7.8) + self.assertEqual(sample.sumComplexPair((cpx1, cpx2)), cpx1 + cpx2) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/derived_test.py b/tests/samplebinding/derived_test.py new file mode 100755 index 000000000..22d49470f --- /dev/null +++ b/tests/samplebinding/derived_test.py @@ -0,0 +1,113 @@ +#!/usr/bin/python + +'''Test cases for Derived class''' + +import sys +import unittest + +import sample +from sample import Abstract, Derived, PolymorphicFuncEnum + +class Deviant(Derived): + def __init__(self): + Derived.__init__(self) + self.pure_virtual_called = False + self.unpure_virtual_called = False + + def pureVirtual(self): + self.pure_virtual_called = True + + def unpureVirtual(self): + self.unpure_virtual_called = True + + def className(self): + return 'Deviant' + +class DerivedTest(unittest.TestCase): + '''Test case for Derived class''' + + def testParentClassMethodsAvailability(self): + '''Test if Derived class really inherits its methods from parent.''' + inherited_methods = set(['callPureVirtual', 'callUnpureVirtual', + 'id_', 'pureVirtual', 'unpureVirtual']) + self.assert_(inherited_methods.issubset(dir(Derived))) + + def testPolymorphicMethodCall(self): + '''Test if the correct polymorphic method is being called.''' + derived = Derived() + + result = derived.polymorphic(1, 2) + self.assertEqual(type(result), PolymorphicFuncEnum) + self.assertEqual(result, sample.PolymorphicFunc_ii) + + result = derived.polymorphic(3) + self.assertEqual(type(result), PolymorphicFuncEnum) + self.assertEqual(result, sample.PolymorphicFunc_ii) + + result = derived.polymorphic(4.4) + self.assertEqual(type(result), PolymorphicFuncEnum) + self.assertEqual(result, sample.PolymorphicFunc_d) + + def testOtherPolymorphicMethodCall(self): + '''Another test to check polymorphic method calling, just to double check.''' + derived = Derived() + + result = derived.otherPolymorphic(1, 2, True, 3.3) + self.assertEqual(type(result), Derived.OtherPolymorphicFuncEnum) + self.assertEqual(result, sample.Derived.OtherPolymorphicFunc_iibd) + + result = derived.otherPolymorphic(1, 2.2) + self.assertEqual(type(result), Derived.OtherPolymorphicFuncEnum) + self.assertEqual(result, Derived.OtherPolymorphicFunc_id) + + def testPolymorphicMethodCallWithDifferentNumericTypes(self): + '''Test if the correct polymorphic method accepts a different numeric type as argument.''' + derived = Derived() + result = derived.polymorphic(1.1, 2.2) + self.assertEqual(type(result), PolymorphicFuncEnum) + self.assertEqual(result, sample.PolymorphicFunc_ii) + + def testPolymorphicMethodCallWithWrongNumberOfArguments(self): + '''Test if a call to a polymorphic method with the wrong number of arguments raises an exception.''' + derived = Derived() + self.assertRaises(TypeError, lambda : derived.otherPolymorphic(1, 2, True)) + + def testReimplementedPureVirtualMethodCall(self): + '''Test if a Python override of a implemented pure virtual method is correctly called from C++.''' + d = Deviant() + d.callPureVirtual() + self.assert_(d.pure_virtual_called) + + def testReimplementedVirtualMethodCall(self): + '''Test if a Python override of a reimplemented virtual method is correctly called from C++.''' + d = Deviant() + d.callUnpureVirtual() + self.assert_(d.unpure_virtual_called) + + def testVirtualMethodCallString(self): + '''Test virtual method call returning string.''' + d = Derived() + self.assertEqual(d.className(), 'Derived') + self.assertEqual(d.getClassName(), 'Derived') + + def testReimplementedVirtualMethodCallReturningString(self): + '''Test if a Python override of a reimplemented virtual method is correctly called from C++.''' + d = Deviant() + self.assertEqual(d.className(), 'Deviant') + self.assertEqual(d.getClassName(), 'Deviant') + + def testSingleArgument(self): + '''Test singleArgument call.''' + d = Derived() + self.assert_(d.singleArgument(False)) + self.assert_(not d.singleArgument(True)) + + def testMethodCallWithDefaultValue(self): + '''Test method call with default value.''' + d = Derived() + self.assertEqual(d.defaultValue(3), 3.1) + self.assertEqual(d.defaultValue(), 0.1) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/enum_test.py b/tests/samplebinding/enum_test.py new file mode 100755 index 000000000..699adceda --- /dev/null +++ b/tests/samplebinding/enum_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +'''Test cases for Python representation of C++ enums''' + +import sys +import unittest + +from sample import SampleNamespace + +class EnumTest(unittest.TestCase): + '''Test case for Abstract class''' + + def testPassingIntegerOnEnumArgument(self): + '''Test if replacing an enum argument with an integer raises an exception.''' + self.assertRaises(TypeError, lambda : SampleNamespace.getNumber(1)) + + def testExtendingEnum(self): + '''Test if can create new items for an enum declared as extensible on the typesystem file.''' + name, value = 'NewItem', 13 + enumitem = SampleNamespace.Option(name, value) + self.assert_(type(enumitem), SampleNamespace.Option) + self.assert_(enumitem.name, name) + self.assert_(int(enumitem), value) + + def testExtendingNonExtensibleEnum(self): + '''Test if trying to create a new enum item for an unextensible enum raises an exception.''' + self.assertRaises(TypeError, lambda : SampleNamespace.InValue(13)) + + def testEnumConversionToAndFromPython(self): + '''Test conversion of enum objects to Python and C++ in both directions.''' + enumout = SampleNamespace.enumInEnumOut(SampleNamespace.TwoIn) + self.assert_(enumout, SampleNamespace.TwoOut) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/global.h b/tests/samplebinding/global.h new file mode 100644 index 000000000..b5ae6a8a0 --- /dev/null +++ b/tests/samplebinding/global.h @@ -0,0 +1,14 @@ +#include "abstract.h" +#include "derived.h" +#include "point.h" +#include "size.h" +#include "complex.h" +#include "functions.h" +#include "kindergarten.h" +#include "pairuser.h" +#include "listuser.h" +#include "samplenamespace.h" +#include "modifications.h" +#include "implicitconv.h" +#include "reference.h" + diff --git a/tests/samplebinding/implicitconv_test.py b/tests/samplebinding/implicitconv_test.py new file mode 100755 index 000000000..30dd870e3 --- /dev/null +++ b/tests/samplebinding/implicitconv_test.py @@ -0,0 +1,27 @@ +#!/usr/bin/python + +'''Test cases for implicit conversions''' + +import sys +import unittest + +from sample import ImplicitConv + +class ImplicitConvTest(unittest.TestCase): + '''Test case for implicit conversions''' + + def testImplicitConversions(self): + '''Test if polymorphic function call decisor takes implicit conversions into account.''' + ic = ImplicitConv.implicitConvCommon(ImplicitConv()) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorNone) + + ic = ImplicitConv.implicitConvCommon(3) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorOne) + self.assertEqual(ic.objId(), 3) + + ic = ImplicitConv.implicitConvCommon(ImplicitConv.CtorThree) + self.assertEqual(ic.ctorEnum(), ImplicitConv.CtorThree) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/list_conversions.h b/tests/samplebinding/list_conversions.h new file mode 100644 index 000000000..f512f98ad --- /dev/null +++ b/tests/samplebinding/list_conversions.h @@ -0,0 +1,29 @@ +template +struct Converter_std_list +{ + static PyObject* toPython(ValueHolder holder) + { + PyObject* result = PyList_New((int) holder.value.size()); + typedef typename StdList::iterator IT; + IT it; + int idx = 0; + for (it = holder.value.begin(); it != holder.value.end(); it++) { + ValueHolder vh(*it); + PyList_SET_ITEM(result, idx, Converter::toPython(vh)); + idx++; + } + return result; + } + static StdList toCpp(PyObject* pyobj) + { + StdList result; + for (int i = 0; i < PyTuple_GET_SIZE(pyobj); i++) { + PyObject* pyItem = PyTuple_GET_ITEM(pyobj, i); + result.push_back(Converter::toCpp(pyItem)); + } + return result; + } +}; + +template +struct Converter > : Converter_std_list > {}; diff --git a/tests/samplebinding/modifications_test.py b/tests/samplebinding/modifications_test.py new file mode 100755 index 000000000..33d420f37 --- /dev/null +++ b/tests/samplebinding/modifications_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/python + +'''Test cases for method modifications performed as described on typesystem. ''' + +import sys +import unittest + +from sample import Modifications, Point + +class ExtModifications(Modifications): + def __init__(self): + Modifications.__init__(self) + + def name(self): + return 'ExtModifications' + + +class ModificationsTest(unittest.TestCase): + '''Test cases for method modifications performed as described on typesystem. ''' + + def setUp(self): + self.mods = Modifications() + + def tearDown(self): + del self.mods + + def testClassMembersAvailability(self): + '''Test if Modified class really have the expected members.''' + expected_members = set(['PolymorphicModFunc', 'PolymorphicNone', + 'Polymorphic_ibiP', 'Polymorphic_ibib', + 'Polymorphic_ibid', 'Polymorphic_ibii', + 'calculateArea', 'doublePlus', 'increment', + 'multiplyPointCoordsPlusValue', 'name', + 'pointToPair', 'polymorphic', 'power', + 'timesTen']) + self.assert_(expected_members.issubset(dir(Modifications))) + + def testRenamedMethodAvailability(self): + '''Test if Modification class really have renamed the 'className' virtual method to 'name'.''' + self.assert_('className' not in dir(Modifications)) + self.assert_('name' in dir(Modifications)) + + def testReimplementationOfRenamedVirtualMethod(self): + '''Test if class inheriting from Modification class have the reimplementation of renamed virtual method called.''' + em = ExtModifications() + self.assertEqual(self.mods.name(), 'Modifications') + self.assertEqual(em.name(), 'ExtModifications') + + def testRegularMethodRenaming(self): + '''Test if Modifications::cppMultiply was correctly renamed to calculateArea.''' + self.assert_('cppMultiply' not in dir(Modifications)) + self.assert_('calculateArea' in dir(Modifications)) + self.assertEqual(self.mods.calculateArea(3, 6), 3 * 6) + + def testRegularMethodRemoval(self): + '''Test if 'Modifications::exclusiveCppStuff' was removed from Python bindings.''' + self.assert_('exclusiveCppStuff' not in dir(Modifications)) + + def testArgumentRemoval(self): + '''Test if second argument of Modifications::doublePlus(int, int) was removed.''' + self.assertRaises(TypeError, lambda : self.mods.doublePlus(3, 7)) + self.assertEqual(self.mods.doublePlus(7), 14) + + def testDefaultValueRemoval(self): + '''Test if default value was removed from first argument of Modifications::increment(int).''' + self.assertRaises(TypeError, self.mods.increment) + self.assertEqual(self.mods.increment(7), 8) + + def testDefaultValueReplacement(self): + '''Test if default values for both arguments of Modifications::power(int, int) were modified.''' + # original default values: int power(int base = 1, int exponent = 0); + self.assertNotEqual(self.mods.power(4), 1) + # modified default values: int power(int base = 2, int exponent = 1); + self.assertEqual(self.mods.power(), 2) + self.assertEqual(self.mods.power(3), 3) + self.assertEqual(self.mods.power(5, 3), 5**3) + + def testSetNewDefaultValue(self): + '''Test if default value was correctly set to 10 for first argument of Modifications::timesTen(int).''' + self.assertEqual(self.mods.timesTen(7), 70) + self.assertEqual(self.mods.timesTen(), 100) + + def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates1(self): + '''Test modifications to method signature and return value using typesystem templates (case 1).''' + result, ok = self.mods.pointToPair(Point(2, 5)) + self.assertEqual(type(ok), bool) + self.assertEqual(type(result), tuple) + self.assertEqual(len(result), 2) + self.assertEqual(type(result[0]), float) + self.assertEqual(type(result[1]), float) + self.assertEqual(result[0], 2.0) + self.assertEqual(result[1], 5.0) + + def testArgumentRemovalAndReturnTypeModificationWithTypesystemTemplates2(self): + '''Test modifications to method signature and return value using typesystem templates (case 2).''' + result, ok = self.mods.multiplyPointCoordsPlusValue(Point(2, 5), 4.1) + self.assertEqual(type(ok), bool) + self.assertEqual(type(result), float) + self.assertEqual(result, 14.1) + + def testPolymorphicMethodModifications(self): + '''Tests modifications to a polymorphic method''' + # polymorphic(int, bool[removed], int, double) + self.assertEqual(self.mods.polymorphic(1, 2, 3.1), Modifications.Polymorphic_ibid) + # polymorphic(int, bool, int[removed,default=321], int) + self.assertEqual(self.mods.polymorphic(1, True, 2), Modifications.Polymorphic_ibii) + # the others weren't modified + self.assertEqual(self.mods.polymorphic(1, True, 2, False), Modifications.Polymorphic_ibib) + self.assertEqual(self.mods.polymorphic(1, False, 2, Point(3, 4)), Modifications.Polymorphic_ibiP) + self.assertRaises(TypeError, lambda : self.mods.polymorphic(1, True, Point(2, 3), Point(4, 5))) + self.assertEqual(self.mods.poly(1, True, Point(2, 3), Point(4, 5)), Modifications.Polymorphic_ibPP) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/pair_conversions.h b/tests/samplebinding/pair_conversions.h new file mode 100644 index 000000000..8adf2f6f3 --- /dev/null +++ b/tests/samplebinding/pair_conversions.h @@ -0,0 +1,25 @@ +template +struct Converter_std_pair +{ + static PyObject* toPython(ValueHolder holder) + { + ValueHolder first(holder.value.first); + ValueHolder second(holder.value.second); + PyObject* tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, Converter::toPython(first)); + PyTuple_SET_ITEM(tuple, 1, Converter::toPython(second)); + return tuple; + } + static StdPair toCpp(PyObject* pyobj) + { + StdPair result; + PyObject* pyFirst = PyTuple_GET_ITEM(pyobj, 0); + PyObject* pySecond = PyTuple_GET_ITEM(pyobj, 1); + result.first = Converter::toCpp(pyFirst); + result.second = Converter::toCpp(pySecond); + return result; + } +}; + +template +struct Converter > : Converter_std_pair > {}; diff --git a/tests/samplebinding/point_test.py b/tests/samplebinding/point_test.py new file mode 100755 index 000000000..69ef1eefb --- /dev/null +++ b/tests/samplebinding/point_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +'''Test cases for Point class''' + +import sys +import unittest + +from sample import Point + +class PointTest(unittest.TestCase): + '''Test case for Point class, including operator overloads.''' + + def testConstructor(self): + '''Test Point class constructor.''' + pt = Point(5.0, 2.3) + self.assertEqual(pt.x(), 5.0) + self.assertEqual(pt.y(), 2.3) + + def testPlusOperator(self): + '''Test Point class + operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(0.5, 3.2) + self.assertEqual(pt1 + pt2, Point(5.0 + 0.5, 2.3 + 3.2)) + + def testEqualOperator(self): + '''Test Point class == operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(5.0, 2.3) + pt3 = Point(0.5, 3.2) + self.assertTrue(pt1 == pt1) + self.assertTrue(pt1 == pt2) + self.assertFalse(pt1 == pt3) + + def testNotEqualOperator(self): + '''Test Point class != operator.''' + pt1 = Point(5.0, 2.3) + pt2 = Point(5.0, 2.3) + self.assertRaises(NotImplementedError, lambda : pt1.__ne__(pt2)) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/reference_test.py b/tests/samplebinding/reference_test.py new file mode 100755 index 000000000..ed4c5cbc8 --- /dev/null +++ b/tests/samplebinding/reference_test.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +'''Test cases for methods that receive references to objects.''' + +import sys +import unittest + +from sample import Reference + +class ReferenceTest(unittest.TestCase): + '''Test case for methods that receive references to objects.''' + + def testMethodThatReceivesReference(self): + '''Test a method that receives a reference to an object as argument.''' + objId = 123 + r = Reference(objId) + self.assertEqual(Reference.usesReference(r), objId) + + def testMethodThatReceivesConstReference(self): + '''Test a method that receives a const reference to an object as argument.''' + objId = 123 + r = Reference(objId) + self.assertEqual(Reference.usesConstReference(r), objId) + + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/sample_test.py b/tests/samplebinding/sample_test.py new file mode 100755 index 000000000..9de201a24 --- /dev/null +++ b/tests/samplebinding/sample_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/python + +'''Test cases for libsample bindings module''' + +import sys +import unittest + +import sample + +class ModuleTest(unittest.TestCase): + '''Test case for module and global functions''' + + def testModuleMembers(self): + '''Test availability of classes, global functions and other members on binding''' + expected_members = set(['Abstract', 'Derived', 'ListUser', 'PairUser', + 'Point', 'gimmeComplexList', 'gimmeDouble', + 'gimmeInt', 'makeCString', 'multiplyPair', + 'returnCString', 'transmuteComplexIntoPoint', + 'transmutePointIntoComplex', 'sumComplexPair', + 'SampleNamespace', 'GlobalEnum', 'NoThing', + 'FirstThing', 'SecondThing', 'ThirdThing']) + self.assert_(expected_members.issubset(dir(sample))) + + def testAbstractPrintFormatEnum(self): + '''Test availability of PrintFormat enum from Abstract class''' + enum_members = set(['PrintFormat', 'Short', 'Verbose', + 'OnlyId', 'ClassNameAndId']) + self.assert_(enum_members.issubset(dir(sample.Abstract))) + + def testSampleNamespaceOptionEnum(self): + '''Test availability of Option enum from SampleNamespace namespace''' + enum_members = set(['Option', 'None', 'RandomNumber', 'UnixTime']) + self.assert_(enum_members.issubset(dir(sample.SampleNamespace))) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/size_test.py b/tests/samplebinding/size_test.py new file mode 100755 index 000000000..dcfc9d9d3 --- /dev/null +++ b/tests/samplebinding/size_test.py @@ -0,0 +1,83 @@ +#!/usr/bin/python + +'''Test cases for operator overloads on Size class''' + +import sys +import unittest + +from sample import Size + +class PointTest(unittest.TestCase): + '''Test case for Size class, including operator overloads.''' + + def testConstructor(self): + '''Test Size class constructor.''' + width, height = (5.0, 2.3) + size = Size(width, height) + self.assertEqual(size.width(), width) + self.assertEqual(size.height(), height) + self.assertEqual(size.calculateArea(), width * height) + + def testPlusOperator(self): + '''Test Size class + operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertEqual(s1 + s2, Size(5.0 + 0.5, 2.3 + 3.2)) + + def testEqualOperator(self): + '''Test Size class == operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 == s1) + self.assertTrue(s1 == s2) + self.assertFalse(s1 == s3) + + def testNotEqualOperator(self): + '''Test Size class != operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertFalse(s1 != s1) + self.assertFalse(s1 != s2) + self.assertTrue(s1 != s3) + + def testMinorEqualOperator(self): + '''Test Size class <= operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 <= s1) + self.assertTrue(s1 <= s2) + self.assertTrue(s3 <= s1) + self.assertFalse(s1 <= s3) + + def testMinorOperator(self): + '''Test Size class < operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertFalse(s1 < s1) + self.assertFalse(s1 < s2) + self.assertTrue(s2 < s1) + + def testMajorEqualOperator(self): + '''Test Size class >= operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(5.0, 2.3) + s3 = Size(0.5, 3.2) + self.assertTrue(s1 >= s1) + self.assertTrue(s1 >= s2) + self.assertTrue(s1 >= s3) + self.assertFalse(s3 >= s1) + + def testMajorOperator(self): + '''Test Size class > operator.''' + s1 = Size(5.0, 2.3) + s2 = Size(0.5, 3.2) + self.assertFalse(s1 > s1) + self.assertTrue(s1 > s2) + self.assertFalse(s2 > s1) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/samplebinding/typesystem_sample.xml b/tests/samplebinding/typesystem_sample.xml new file mode 100644 index 000000000..c049ea9eb --- /dev/null +++ b/tests/samplebinding/typesystem_sample.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %0 = ((%TYPE*) ((Shiboken::PyBaseWrapper*) self)->cptr)-> + %TYPE::%FUNCTION_NAME(%1, true, %3, %4); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3