diff options
Diffstat (limited to 'examples/samplebinding')
-rw-r--r-- | examples/samplebinding/CMakeLists.txt | 67 | ||||
-rw-r--r-- | examples/samplebinding/README.md | 236 | ||||
-rw-r--r-- | examples/samplebinding/bindings.h | 51 | ||||
-rw-r--r-- | examples/samplebinding/bindings.xml | 54 | ||||
-rw-r--r-- | examples/samplebinding/doc/bindings.h.rstinc | 2 | ||||
-rw-r--r-- | examples/samplebinding/doc/bindings.xml.rstinc | 31 | ||||
-rw-r--r-- | examples/samplebinding/doc/samplebinding.pyproject | 11 | ||||
-rw-r--r-- | examples/samplebinding/doc/samplebinding.rst | 292 | ||||
-rw-r--r-- | examples/samplebinding/icecream.cpp | 63 | ||||
-rw-r--r-- | examples/samplebinding/icecream.h | 61 | ||||
-rw-r--r-- | examples/samplebinding/macros.h | 51 | ||||
-rw-r--r-- | examples/samplebinding/main.py | 53 | ||||
-rw-r--r-- | examples/samplebinding/truck.cpp | 88 | ||||
-rw-r--r-- | examples/samplebinding/truck.h | 70 |
14 files changed, 448 insertions, 682 deletions
diff --git a/examples/samplebinding/CMakeLists.txt b/examples/samplebinding/CMakeLists.txt index f7651e04f..4807904c1 100644 --- a/examples/samplebinding/CMakeLists.txt +++ b/examples/samplebinding/CMakeLists.txt @@ -1,5 +1,8 @@ -cmake_minimum_required(VERSION 3.16) -cmake_policy(VERSION 3.16) +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.18) +cmake_policy(VERSION 3.18) # Enable policy to not use RPATH settings for install_name on macOS. if(POLICY CMP0068) @@ -11,8 +14,8 @@ project(SampleBinding) # ================================ General configuration ====================================== -# Set CPP standard to C++11 minimum. -set(CMAKE_CXX_STANDARD 11) +# Set CPP standard to C++17 minimum. +set(CMAKE_CXX_STANDARD 17) # The sample library for which we will create bindings. You can change the name to something # relevant for your project. @@ -42,13 +45,27 @@ set(generated_sources # ================================== Shiboken detection ====================================== # Use provided python interpreter if given. if(NOT python_interpreter) - find_program(python_interpreter "python") + if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + find_program(python_interpreter "python_d") + if(NOT python_interpreter) + message(FATAL_ERROR + "A debug Python interpreter could not be found, which is a requirement when " + "building this example in a debug configuration. Make sure python_d.exe is in " + "PATH.") + endif() + else() + find_program(python_interpreter "python") + if(NOT python_interpreter) + message(FATAL_ERROR + "No Python interpreter could be found. Make sure python is in PATH.") + endif() + endif() endif() message(STATUS "Using python interpreter: ${python_interpreter}") # Macro to get various pyside / python include / link flags and paths. -# Uses the not entirely supported utils/pyside2_config.py file. -macro(pyside2_config option output_var) +# Uses the not entirely supported utils/pyside_config.py file. +macro(pyside_config option output_var) if(${ARGC} GREATER 2) set(is_list ${ARGV2}) else() @@ -56,13 +73,13 @@ macro(pyside2_config option output_var) endif() execute_process( - COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside2_config.py" + COMMAND ${python_interpreter} "${CMAKE_SOURCE_DIR}/../utils/pyside_config.py" ${option} OUTPUT_VARIABLE ${output_var} OUTPUT_STRIP_TRAILING_WHITESPACE) if ("${${output_var}}" STREQUAL "") - message(FATAL_ERROR "Error: Calling pyside2_config.py ${option} returned no output.") + message(FATAL_ERROR "Error: Calling pyside_config.py ${option} returned no output.") endif() if(is_list) string (REPLACE " " ";" ${output_var} "${${output_var}}") @@ -70,14 +87,14 @@ macro(pyside2_config option output_var) endmacro() # Query for the shiboken generator path, Python path, include paths and linker flags. -pyside2_config(--shiboken2-module-path shiboken2_module_path) -pyside2_config(--shiboken2-generator-path shiboken2_generator_path) -pyside2_config(--python-include-path python_include_dir) -pyside2_config(--shiboken2-generator-include-path shiboken_include_dir 1) -pyside2_config(--shiboken2-module-shared-libraries-cmake shiboken_shared_libraries 0) -pyside2_config(--python-link-flags-cmake python_linking_data 0) - -set(shiboken_path "${shiboken2_generator_path}/shiboken2${CMAKE_EXECUTABLE_SUFFIX}") +pyside_config(--shiboken-module-path shiboken_module_path) +pyside_config(--shiboken-generator-path shiboken_generator_path) +pyside_config(--python-include-path python_include_dir) +pyside_config(--shiboken-generator-include-path shiboken_include_dir 1) +pyside_config(--shiboken-module-shared-libraries-cmake shiboken_shared_libraries 0) +pyside_config(--python-link-flags-cmake python_linking_data 0) + +set(shiboken_path "${shiboken_generator_path}/shiboken6${CMAKE_EXECUTABLE_SUFFIX}") if(NOT EXISTS ${shiboken_path}) message(FATAL_ERROR "Shiboken executable not found at path: ${shiboken_path}") endif() @@ -93,7 +110,7 @@ endif() # Enable rpaths so that the built shared libraries find their dependencies. set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -set(CMAKE_INSTALL_RPATH ${shiboken2_module_path} ${CMAKE_CURRENT_SOURCE_DIR}) +set(CMAKE_INSTALL_RPATH ${shiboken_module_path} ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # ============================================================================================= # !!! End of dubious section. @@ -118,7 +135,7 @@ target_compile_definitions(${sample_library} PRIVATE BINDINGS_BUILD) # Set up the options to pass to shiboken. set(shiboken_options --generator-set=shiboken --enable-parent-ctor-heuristic - --enable-return-value-heuristic --use-isnull-as-nb_nonzero + --enable-return-value-heuristic --use-isnull-as-nb-bool --avoid-protected-hack -I${CMAKE_SOURCE_DIR} -T${CMAKE_SOURCE_DIR} @@ -159,7 +176,11 @@ set_property(TARGET ${bindings_library} PROPERTY PREFIX "") set_property(TARGET ${bindings_library} PROPERTY OUTPUT_NAME "${bindings_library}${PYTHON_EXTENSION_SUFFIX}") if(WIN32) - set_property(TARGET ${bindings_library} PROPERTY SUFFIX ".pyd") + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set_property(TARGET ${bindings_library} PROPERTY SUFFIX "_d.pyd") + else() + set_property(TARGET ${bindings_library} PROPERTY SUFFIX ".pyd") + endif() endif() # Make sure the linker doesn't complain about not finding Python symbols on macOS. @@ -191,7 +212,7 @@ if(WIN32) # Circumvent some "#pragma comment(lib)"s in "include/pyconfig.h" which might force to link # against a wrong python shared library. - set(python_versions_list 3 32 33 34 35 36 37 38) + set(python_versions_list 3 36 37 38 39) set(python_additional_link_flags "") foreach(ver ${python_versions_list}) set(python_additional_link_flags @@ -204,7 +225,7 @@ if(WIN32) PROPERTIES LINK_FLAGS "${python_additional_link_flags}") # Compile a list of shiboken shared libraries to be installed, so that - # the user doesn't have to set the PATH manually to point to the PySide2 package. + # the user doesn't have to set the PATH manually to point to the PySide6 package. foreach(library_path ${shiboken_shared_libraries}) string(REGEX REPLACE ".lib$" ".dll" library_path ${library_path}) file(TO_CMAKE_PATH ${library_path} library_path) @@ -224,7 +245,7 @@ endif() install(TARGETS ${bindings_library} ${sample_library} LIBRARY DESTINATION ${CMAKE_CURRENT_SOURCE_DIR} RUNTIME DESTINATION ${CMAKE_CURRENT_SOURCE_DIR} - ) + ) install(FILES ${windows_shiboken_shared_libraries} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}) # ============================================================================================= # !!! End of dubious section. diff --git a/examples/samplebinding/README.md b/examples/samplebinding/README.md deleted file mode 100644 index e84d1eff4..000000000 --- a/examples/samplebinding/README.md +++ /dev/null @@ -1,236 +0,0 @@ -# Sample bindings example - -This example showcases how to generate Python bindings for a -non-Qt C++ library. - -The example defines a CMake project that builds two libraries: -* `libuniverse` - a sample library with two C++ classes. -* `Universe` - the generated Python extension module that contains - bindings to the library above. - -The project file is structured in such a way that a user can copy-paste -in into their own project, and be able to build it with a minimal amount -of modifications. - -## Description - -The libuniverse library declares two classes: `Icecream` and `Truck`. - -`Icecream` objects have a flavor, and an accessor for returning the -flavor. - -`Truck` instances store a vector of `Icecream` objects, and have various -methods for adding new flavors, printing available flavors, delivering -icecream, etc. - -From a C++ perspective, `Icecream` instances are treated as -**object types** (pointer semantics) because the class declares virtual -methods. - -In contrast `Truck` does not define virtual methods and is treated as -a **value type** (copy semantics). - -Because `Truck` is a value type and it stores a vector of `Icecream` -pointers, the rule of three has to be taken into account (implement the -copy constructor, assignment operator, destructor). - -And due to `Icecream` objects being copyable, the type has to define an -implementation of the *clone()* method, to avoid type slicing issues. - -Both of these types and their methods will be exposed to Python by -generating CPython code. The code is generated by **shiboken** and -placed in separate ".cpp" files named after each C++ type. The code is -then compiled and linked into a shared library. The shared library is a -CPython extension module, which is loaded by the Python interpreter. - -Beacuse the C++ language has different semantics to Python, shiboken -needs help in figuring out how to generate the bindings code. This is -done by specifying a special XML file called a typesystem file. - -In the typesystem file you specify things like: - * which C++ primitive types should have bindings (int, bool, float) - * which C++ classes should have bindings (Icecream) and what kind of - semantics (value / object) - * Ownership rules (who deletes the C++ objects, C++ or Python) - * Code injection (for various special cases that shiboken doesn't know - about) - * Package name (name of package as imported from Python) - -In this example we declare `bool` and `std::string` as primitive types, -`Icecream` as an object type, `Truck` as a value type, -and the `clone()` and `addIcecreamFlavor(Icecream*)` need additional -info about who owns the parameter objects when passing them across -language boundaries (in this case C++ will delete the objects). - -The `Truck` has getters and setters for the string `arrivalMessage`. -In the type system file, we declare this to be a property in Python: - -``` -<property type="std::string" name="arrivalMessage" get="getArrivalMessage" set="setArrivalMessage"/> -``` - -It can then be used in a more pythonic way: - -``` -special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" -``` - -After shiboken generates the C++ code and CMake makes an extension -module from the code, the types can be accessed in Python simply by -importing them using the original C++ names. - -``` -from Universe import Icecream, Truck -``` - -Constructing C++ wrapped objects is the same as in Python -``` -icecream = Icecream("vanilla") -truck = Truck() -``` - - -And actual C++ constructors are mapped to the Python `__init__` method. -``` -class VanillaChocolateIcecream(Icecream): - def __init__(self, flavor=""): - super(VanillaChocolateIcecream, self).__init__(flavor) -``` - - -C++ methods can be accessed as regular Python methods using the C++ -names -``` -truck.addIcecreamFlavor(icecream) -``` - - -Inheritance works as with regular Python classes, and virtual C++ -methods can be overridden simply by definining a method with the same -name as in the C++ class. -``` -class VanillaChocolateIcecream(Icecream): - # ... - def getFlavor(self): - return "vanilla sprinked with chocolate" - -``` - - -The `main.py` script demonstrates usages of these types. - -The CMake project file contains many comments explaining all the build -rules for those interested in the build process. - -## Building the project - -This example can only be built using **CMake**. -The following requirements need to be met: - -* A PySide2 package is installed into the current active Python - environment (system or virtualenv) -* A new enough version of CMake (**3.1+**). - -For Windows you will also need: -* a Visual Studio environment to be active in your terminal -* Correct visual studio architecture chosen (32 vs 64 bit) -* Make sure that your Python intepreter and bindings project build - configuration is the same (all Release, which is more likely, - or all Debug). - -The build uses the `pyside2_config.py` file to configure the project -using the current PySide2/Shiboken2 installation. - -### Using CMake - -You can build and run this example by executing the following commands -(slightly adapted to your file system layout) in a terminal: - -On macOS/Linux: -```bash -cd ~/pyside-setup/examples/samplebinding -mkdir build -cd build -cmake -H.. -B. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -make -make install -python ../main.py -``` - -On Windows: -```bash -cd C:\pyside-setup\examples\samplebinding -mkdir build -cd build -cmake -H.. -B. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -# or if you have jom available -# cmake -H.. -B. -G "NMake Makefiles JOM" -DCMAKE_BUILD_TYPE=Release -nmake # or jom -nmake install # or jom install -python ..\main.py -``` - -#### Windows troubleshooting - -It is possible that **CMake** can pick up the wrong compiler -for a different architecture, but it can be addressed explicitly -using the -G option: - -```bash -cmake -H.. -B. -G "Visual Studio 14 Win64" -``` - -If the `-G "Visual Studio 14 Win64"` option is used, a `sln` file -will be generated, and can be used with `MSBuild` -instead of `nmake/jom`. -The easiest way to both build and install in this case, is to use -the cmake executable: - -```bash -cmake --build . --target install --config Release -``` - -Note that using the "NMake Makefiles JOM" generator is preferred to -the MSBuild one, because the MSBuild one generates configs for both -Debug and Release, and this might lead to building errors if you -accidentally build the wrong config at least once. - -## Virtualenv Support - -If the python application is started from a terminal with an activated -python virtual environment, that environment's packages will be used for -the python module import process. -In this case, make sure that the bindings were built while the -`virtualenv` was active, so that the build system picks up the correct -python shared library and PySide2 / shiboken package. - -## Linux Shared Libraries Notes - -For this example's purpose, we link against the absolute path of the -dependent shared library `libshiboken` because the -installation of the library is done via a wheel, and there is -no clean solution to include symbolic links in a wheel package -(so that passing -lshiboken to the linker would work). - -## Windows Notes - -The build config of the bindings (Debug or Release) should match -the PySide2 build config, otherwise the application will not properly -work. - -In practice this means the only supported configurations are: - -1. release config build of the bindings + - PySide2 `setup.py` without `--debug` flag + `python.exe` for the - PySide2 build process + `python36.dll` for the linked in shared - library. -2. debug config build of the application + - PySide2 `setup.py` **with** `--debug` flag + `python_d.exe` for the - PySide2 build process + `python36_d.dll` for the linked in shared - library. - -This is necessary because all the shared libraries in question have to -link to the same C++ runtime library (`msvcrt.dll` or `msvcrtd.dll`). -To make the example as self-contained as possible, the shared libraries -in use (`pyside2.dll`, `shiboken2.dll`) are hard-linked into the build -folder of the application. diff --git a/examples/samplebinding/bindings.h b/examples/samplebinding/bindings.h index ba42dc626..36f0b86be 100644 --- a/examples/samplebinding/bindings.h +++ b/examples/samplebinding/bindings.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef BINDINGS_H #define BINDINGS_H diff --git a/examples/samplebinding/bindings.xml b/examples/samplebinding/bindings.xml index 9be9f1afa..fe5b4e5e2 100644 --- a/examples/samplebinding/bindings.xml +++ b/examples/samplebinding/bindings.xml @@ -1,60 +1,10 @@ <?xml version="1.0"?> <!-- -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause --> <typesystem package="Universe"> - <primitive-type name="bool"/> - <primitive-type name="std::string"/> - <object-type name="Icecream"> <!-- By default the ownership of an object created in Python is tied to the Python name pointing to it. In order for the underlying diff --git a/examples/samplebinding/doc/bindings.h.rstinc b/examples/samplebinding/doc/bindings.h.rstinc new file mode 100644 index 000000000..e2a0b6fef --- /dev/null +++ b/examples/samplebinding/doc/bindings.h.rstinc @@ -0,0 +1,2 @@ +The Shiboken generator needs a header file that includes +the types we are interested in: diff --git a/examples/samplebinding/doc/bindings.xml.rstinc b/examples/samplebinding/doc/bindings.xml.rstinc new file mode 100644 index 000000000..60b9b1a48 --- /dev/null +++ b/examples/samplebinding/doc/bindings.xml.rstinc @@ -0,0 +1,31 @@ +Shiboken requires an XML-based typesystem file that defines the +relationship between C++ and Python types. + +It declares the two aforementioned classes. One of them as an +“object-type” and the other as a “value-type”. The main difference is that +object-types are passed around in generated code as pointers, whereas +value-types are copied (value semantics). + +By specifying the names of these classes in the typesystem file, Shiboken +automatically tries to generate bindings for all methods of those +classes. You need not mention all the methods manually in the XML file, unless +you want to modify them. + +**Object ownership rules** + +Shiboken doesn't know if Python or C++ are responsible for freeing the C++ +objects that were allocated in the Python code, and assuming this might lead to +errors. There can be cases where Python should release the C++ memory when the +reference count of the Python object becomes zero, but it should never delete +the underlying C++ object just from assuming that it will not be deleted by +underlying C++ library, or if it's maybe parented to another object (like +QWidgets). + +In our case, the :code:`clone()` method is only called inside the C++ library, +and we assume that the C++ code takes care of releasing the cloned object. + +As for :code:`addIcecreamFlavor()`, we know that a :code:`Truck` owns the +:code:`Icecream` object, and will remove it once the :code:`Truck` is +destroyed. That's why the ownership is set to “c++” in the typesystem file, +so that the C++ objects are not deleted when the corresponding Python names +go out of scope. diff --git a/examples/samplebinding/doc/samplebinding.pyproject b/examples/samplebinding/doc/samplebinding.pyproject new file mode 100644 index 000000000..b0786355f --- /dev/null +++ b/examples/samplebinding/doc/samplebinding.pyproject @@ -0,0 +1,11 @@ +{ + "files": ["../bindings.h", + "../bindings.xml", + "../icecream.cpp", + "../icecream.h", + "../macros.h", + "../main.py", + "../truck.cpp", + "../truck.h", + "../CMakeLists.txt"] +} diff --git a/examples/samplebinding/doc/samplebinding.rst b/examples/samplebinding/doc/samplebinding.rst new file mode 100644 index 000000000..51b6b4c20 --- /dev/null +++ b/examples/samplebinding/doc/samplebinding.rst @@ -0,0 +1,292 @@ +Sample Bindings Example +======================= + +This example showcases how to generate Python bindings for a +non-Qt C++ library. + +The example defines a CMake project that builds two libraries: + +* ``libuniverse`` - a sample library with two C++ classes. + +* ``Universe`` - the generated Python extension module that contains + bindings to the library above. + +The project file is structured in such a way that a user can copy-paste +in into their own project, and be able to build it with a minimal amount +of modifications. + +Description ++++++++++++ + +The libuniverse library declares two classes: ``Icecream`` and ``Truck``. + +``Icecream`` objects have a flavor, and an accessor for returning the +flavor. + +``Truck`` instances store a vector of ``Icecream`` objects, and have various +methods for adding new flavors, printing available flavors, delivering +icecream, etc. + +From a C++ perspective, ``Icecream`` instances are treated as +*object types* (pointer semantics) because the class declares virtual +methods. + +In contrast ``Truck`` does not define virtual methods and is treated as +a *value type* (copy semantics). + +Because ``Truck`` is a value type and it stores a vector of ``Icecream`` +pointers, the rule of five has to be taken into account (implement the +copy constructor, assignment operator, move constructor, move assignment +operator and destructor). + +And due to ``Icecream`` objects being copyable, the type has to define an +implementation of the ``clone()`` method, to avoid type slicing issues. + +Both of these types and their methods will be exposed to Python by +generating CPython code. The code is generated by ``shiboken`` and +placed in separate ``.cpp`` files named after each C++ type. The code is +then compiled and linked into a shared library. The shared library is a +CPython extension module, which is loaded by the Python interpreter. + +Beacuse the C++ language has different semantics to Python, shiboken +needs help in figuring out how to generate the bindings code. This is +done by specifying a special XML file called a typesystem file. + +In the typesystem file you specify things like: + + * Which C++ classes should have bindings (Icecream, Truck) and with what + kind of semantics (value / object) + + * Ownership rules (who deletes the C++ objects, C++ or Python) + + * Code injection (for various special cases that shiboken doesn't know + about) + + * Package name (name of package as imported from Python) + +In this example we declare ``Icecream`` as an object type and ``Truck`` +as a value type. The ``clone()`` and ``addIcecreamFlavor(Icecream*)`` +need additional info about who owns the parameter objects when passing +them across language boundaries (in this case C++ will delete the objects). + +The ``Truck`` has getters and setters for the string ``arrivalMessage``. +In the type system file, we declare this to be a property in Python: + +.. code-block:: xml + + <property type="std::string" name="arrivalMessage" get="getArrivalMessage" set="setArrivalMessage"/> + + +It can then be used in a more pythonic way: + +.. code-block:: python + + special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" + +After shiboken generates the C++ code and CMake makes an extension +module from the code, the types can be accessed in Python simply by +importing them using the original C++ names. + +.. code-block:: python + + from Universe import Icecream, Truck + + +Constructing C++ wrapped objects is the same as in Python + +.. code-block:: python + + icecream = Icecream("vanilla") + truck = Truck() + + +And actual C++ constructors are mapped to the Python `__init__` method. + +.. code-block:: python + + class VanillaChocolateIcecream(Icecream): + def __init__(self, flavor=""): + super().__init__(flavor) + + +C++ methods can be accessed as regular Python methods using the C++ +names + +.. code-block:: python + + truck.addIcecreamFlavor(icecream) + +Inheritance works as with regular Python classes, and virtual C++ +methods can be overridden simply by definining a method with the same +name as in the C++ class. + +.. code-block:: python + + class VanillaChocolateIcecream(Icecream): + # ... + def getFlavor(self): + return "vanilla sprinked with chocolate" + + +The ``main.py`` script demonstrates usages of these types. + +The CMake project file contains many comments explaining all the build +rules for those interested in the build process. + +Building the project +++++++++++++++++++++ + +This example can only be built using ``CMake``. +The following requirements need to be met: + +* A PySide package is installed into the current active Python + environment (system or virtualenv) + +* A new enough version of CMake (3.16+). + +* ninja + +For Windows you will also need: + +* a Visual Studio environment to be active in your terminal + +* Correct visual studio architecture chosen (32 vs 64 bit) + +* Make sure that your Python intepreter and bindings project build + configuration is the same (all Release, which is more likely, + or all Debug). + +The build uses the ``pyside_config.py`` file to configure the project +using the current PySide/Shiboken installation. + +Using CMake +=========== + +You can build and run this example by executing the following commands +(slightly adapted to your file system layout) in a terminal: + +Run CMake on macOS/Linux: + +.. code-block:: bash + + cd ~/pyside-setup/examples/samplebinding + mkdir build + cd build + cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release + +Run CMake on Windows: + +.. code-block:: bash + + cd C:\pyside-setup\examples\samplebinding + mkdir build + cd build + cmake .. -B. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl.exe + +To build: + +.. code-block:: bash + ninja + ninja install + cd .. + +Use the Python module ++++++++++++++++++++++ + +The final example can then be run by: + +.. code-block:: bash + + python main.py + +In the ``main.py`` script, two types are derived from :code:`Icecream` for +different “flavors” after importing the classes from the :code:`Universe` +module. Then, a :code:`truck` is created to deliver some regular flavored +Icecreams and two special ones. + +If the delivery fails, a new :code:`truck` is created with the old flavors +copied over, and a new *magical* flavor that will surely satisfy all customers. + +Try running it to see if the ice creams are delivered. + +Windows troubleshooting ++++++++++++++++++++++++ + +It is possible that ``CMake`` can pick up the wrong compiler +for a different architecture, but it can be addressed explicitly +by setting the ``CC`` environment variable: + +.. code-block:: bash + + set CC=cl + +passing the compiler on the command line: + +.. code-block:: bash + + cmake -S.. -B. -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe + +or by using the -G option: + +.. code-block:: bash + + cmake -S.. -B. -G "Visual Studio 14 Win64" + +If the ``-G "Visual Studio 14 Win64"`` option is used, a ``sln`` file +will be generated, and can be used with ``MSBuild`` +instead of ``ninja``. +The easiest way to both build and install in this case, is to use +the cmake executable: + +.. code-block:: bash + + cmake --build . --target install --config Release + +Note that using the ``"Ninja"`` generator is preferred to +the MSBuild one, because the MSBuild one generates configs for both +Debug and Release, and this might lead to building errors if you +accidentally build the wrong config at least once. + +Virtualenv Support +++++++++++++++++++ + +If the python application is started from a terminal with an activated +python virtual environment, that environment's packages will be used for +the python module import process. +In this case, make sure that the bindings were built while the +``virtualenv`` was active, so that the build system picks up the correct +python shared library and PySide6 / shiboken package. + +Linux Shared Libraries Notes +++++++++++++++++++++++++++++ + +For this example's purpose, we link against the absolute path of the +dependent shared library ``libshiboken`` because the +installation of the library is done via a wheel, and there is +no clean solution to include symbolic links in a wheel package +(so that passing -lshiboken to the linker would work). + +Windows Notes ++++++++++++++ + +The build config of the bindings (Debug or Release) should match +the PySide build config, otherwise the application will not properly +work. + +In practice this means the only supported configurations are: + +#. release config build of the bindings + + PySide ``setup.py`` without ``--debug`` flag + ``python.exe`` for the + PySide build process + ``python39.dll`` for the linked in shared + library. + +#. debug config build of the application + + PySide ``setup.py`` *with* ``--debug`` flag + ``python_d.exe`` for the + PySide build process + ``python39_d.dll`` for the linked in shared + library. + +This is necessary because all the shared libraries in question have to +link to the same C++ runtime library (``msvcrt.dll`` or ``msvcrtd.dll``). +To make the example as self-contained as possible, the shared libraries +in use (``pyside6.dll``, ``shiboken6.dll``) are hard-linked into the build +folder of the application. diff --git a/examples/samplebinding/icecream.cpp b/examples/samplebinding/icecream.cpp index 8d40302da..4a81bd08b 100644 --- a/examples/samplebinding/icecream.cpp +++ b/examples/samplebinding/icecream.cpp @@ -1,60 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "icecream.h" +#include <iostream> + Icecream::Icecream(const std::string &flavor) : m_flavor(flavor) {} -Icecream::~Icecream() {} +Icecream::~Icecream() = default; -const std::string Icecream::getFlavor() +std::string Icecream::getFlavor() const { return m_flavor; } @@ -63,3 +18,9 @@ Icecream *Icecream::clone() { return new Icecream(*this); } + +std::ostream &operator<<(std::ostream &str, const Icecream &i) +{ + str << i.getFlavor(); + return str; +} diff --git a/examples/samplebinding/icecream.h b/examples/samplebinding/icecream.h index 1997fdc49..7d5e97d38 100644 --- a/examples/samplebinding/icecream.h +++ b/examples/samplebinding/icecream.h @@ -1,71 +1,26 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef ICECREAM_H #define ICECREAM_H -#include <string> - #include "macros.h" +#include <iosfwd> +#include <string> + class BINDINGS_API Icecream { public: - Icecream(const std::string &flavor); + explicit Icecream(const std::string &flavor); virtual Icecream *clone(); virtual ~Icecream(); - virtual const std::string getFlavor(); + virtual std::string getFlavor() const; private: std::string m_flavor; }; +std::ostream &operator<<(std::ostream &str, const Icecream &i); #endif // ICECREAM_H diff --git a/examples/samplebinding/macros.h b/examples/samplebinding/macros.h index 71b27c398..af1874fa4 100644 --- a/examples/samplebinding/macros.h +++ b/examples/samplebinding/macros.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef MACROS_H #define MACROS_H diff --git a/examples/samplebinding/main.py b/examples/samplebinding/main.py index dc727c5d8..cb9f116ee 100644 --- a/examples/samplebinding/main.py +++ b/examples/samplebinding/main.py @@ -1,53 +1,14 @@ - -############################################################################ -## -## Copyright (C) 2018 The Qt Company Ltd. -## Contact: http://www.qt.io/licensing/ -## -## This file is part of the Qt for Python examples of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:BSD$ -## You may use this file under the terms of the BSD license as follows: -## -## "Redistribution and use in source and binary forms, with or without -## modification, are permitted provided that the following conditions are -## met: -## * Redistributions of source code must retain the above copyright -## notice, this list of conditions and the following disclaimer. -## * Redistributions in binary form must reproduce the above copyright -## notice, this list of conditions and the following disclaimer in -## the documentation and/or other materials provided with the -## distribution. -## * Neither the name of The Qt Company Ltd nor the names of its -## contributors may be used to endorse or promote products derived -## from this software without specific prior written permission. -## -## -## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -## -## $QT_END_LICENSE$ -## -############################################################################ - -from __future__ import print_function +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause """An example showcasing how to use bindings for a custom non-Qt C++ library""" from Universe import Icecream, Truck + class VanillaChocolateIcecream(Icecream): def __init__(self, flavor=""): - super(VanillaChocolateIcecream, self).__init__(flavor) + super().__init__(flavor) def clone(self): return VanillaChocolateIcecream(self.getFlavor()) @@ -55,16 +16,18 @@ class VanillaChocolateIcecream(Icecream): def getFlavor(self): return "vanilla sprinked with chocolate" + class VanillaChocolateCherryIcecream(VanillaChocolateIcecream): def __init__(self, flavor=""): - super(VanillaChocolateIcecream, self).__init__(flavor) + super().__init__(flavor) def clone(self): return VanillaChocolateCherryIcecream(self.getFlavor()) def getFlavor(self): base_flavor = super(VanillaChocolateCherryIcecream, self).getFlavor() - return base_flavor + " and a cherry" + return f"{base_flavor} and a cherry" + if __name__ == '__main__': leave_on_destruction = True diff --git a/examples/samplebinding/truck.cpp b/examples/samplebinding/truck.cpp index 056abfcd6..819d2b1a0 100644 --- a/examples/samplebinding/truck.cpp +++ b/examples/samplebinding/truck.cpp @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <iostream> #include <random> @@ -57,40 +10,38 @@ Truck::Truck(bool leaveOnDestruction) : m_leaveOnDestruction(leaveOnDestruction) Truck::Truck(const Truck &other) { - for (size_t i = 0; i < other.m_flavors.size(); ++i) { - addIcecreamFlavor(other.m_flavors[i]->clone()); - } + assign(other); } Truck &Truck::operator=(const Truck &other) { if (this != &other) { - clearFlavors(); - for (size_t i = 0; i < other.m_flavors.size(); ++i) { - addIcecreamFlavor(other.m_flavors[i]->clone()); - } + m_flavors.clear(); + assign(other); } return *this; } +Truck::Truck(Truck &&other) = default; + +Truck& Truck::operator=(Truck &&other) = default; + Truck::~Truck() { if (m_leaveOnDestruction) leave(); - clearFlavors(); } void Truck::addIcecreamFlavor(Icecream *icecream) { - m_flavors.push_back(icecream); + m_flavors.push_back(IcecreamPtr(icecream)); } void Truck::printAvailableFlavors() const { std::cout << "It sells the following flavors: \n"; - for (size_t i = 0; i < m_flavors.size(); ++ i) { - std::cout << " * " << m_flavors[i]->getFlavor() << '\n'; - } + for (const auto &flavor : m_flavors) + std::cout << " * " << *flavor << '\n'; std::cout << '\n'; } @@ -119,6 +70,13 @@ std::string Truck::getArrivalMessage() const return m_arrivalMessage; } +void Truck::assign(const Truck &other) +{ + m_flavors.reserve(other.m_flavors.size()); + for (const auto &f : other.m_flavors) + m_flavors.push_back(IcecreamPtr(f->clone())); +} + bool Truck::deliver() const { std::random_device rd; @@ -133,11 +91,3 @@ bool Truck::deliver() const return result; } - -void Truck::clearFlavors() -{ - for (size_t i = 0; i < m_flavors.size(); ++i) { - delete m_flavors[i]; - } - m_flavors.clear(); -} diff --git a/examples/samplebinding/truck.h b/examples/samplebinding/truck.h index 3f213f9ac..f7afd1290 100644 --- a/examples/samplebinding/truck.h +++ b/examples/samplebinding/truck.h @@ -1,66 +1,24 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt for Python examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef TRUCK_H #define TRUCK_H -#include <vector> - #include "icecream.h" #include "macros.h" -class BINDINGS_API Truck { +#include <memory> +#include <vector> + +class BINDINGS_API Truck +{ public: - Truck(bool leaveOnDestruction = false); + explicit Truck(bool leaveOnDestruction = false); Truck(const Truck &other); Truck& operator=(const Truck &other); + Truck(Truck &&other); + Truck& operator=(Truck &&other); + ~Truck(); void addIcecreamFlavor(Icecream *icecream); @@ -76,11 +34,13 @@ public: std::string getArrivalMessage() const; private: - void clearFlavors(); + using IcecreamPtr = std::shared_ptr<Icecream>; + + void assign(const Truck &other); bool m_leaveOnDestruction = false; std::string m_arrivalMessage = "A new icecream truck has arrived!\n"; - std::vector<Icecream *> m_flavors; + std::vector<IcecreamPtr> m_flavors; }; #endif // TRUCK_H |