From 8f4c48f77c28253e814de3fe0f5371486398602f Mon Sep 17 00:00:00 2001
From: Renato Araujo Oliveira Filho <renato.araujo@kdab.com>
Date: Mon, 5 Apr 2021 14:25:02 -0300
Subject: [PATCH] Implement unit test for std::optional

Change-Id: I53153557b40d550972416a9cd9e074bf45f5a33b
---
 sources/shiboken2/CMakeLists.txt              |  2 ++
 sources/shiboken2/tests/CMakeLists.txt        |  3 ++
 sources/shiboken2/tests/libsample/point.cpp   |  9 ++++++
 sources/shiboken2/tests/libsample/point.h     |  2 ++
 .../tests/samplebinding/point_test.py         |  5 +++
 .../tests/samplebinding/typesystem_sample.xml | 31 +++++++++++++++++++
 6 files changed, 52 insertions(+)

diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt
index 3de5d3223..d9691c7b0 100644
--- a/sources/shiboken2/CMakeLists.txt
+++ b/sources/shiboken2/CMakeLists.txt
@@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.1)
 cmake_policy(VERSION 3.1)
 
 set(CMAKE_BUILD_TYPE Release CACHE STRING "Build Type")
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 project(shiboken2)
 include(CheckIncludeFileCXX)
diff --git a/sources/shiboken2/tests/CMakeLists.txt b/sources/shiboken2/tests/CMakeLists.txt
index 2ba951de1..86e5bdad8 100644
--- a/sources/shiboken2/tests/CMakeLists.txt
+++ b/sources/shiboken2/tests/CMakeLists.txt
@@ -1,4 +1,6 @@
 cmake_minimum_required(VERSION 3.1)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 if(BUILD_TESTS)
     find_package(Qt${QT_MAJOR_VERSION}Test 5.12 REQUIRED)
@@ -18,6 +20,7 @@ else()
     message(STATUS "Tests will be generated using the protected hack!")
     set(GENERATOR_EXTRA_FLAGS )
 endif()
+set(GENERATOR_EXTRA_FLAGS ${GENERATOR_EXTRA_FLAGS} -std=c++${CMAKE_CXX_STANDARD})
 
 add_subdirectory(minimalbinding)
 if(NOT DEFINED MINIMAL_TESTS)
diff --git a/sources/shiboken2/tests/libsample/point.cpp b/sources/shiboken2/tests/libsample/point.cpp
index 75e015e07..c70260480 100644
--- a/sources/shiboken2/tests/libsample/point.cpp
+++ b/sources/shiboken2/tests/libsample/point.cpp
@@ -153,3 +153,12 @@ transmuteComplexIntoPoint(const Complex& cpx)
     return pt;
 }
 
+std::optional<Point>
+Point::copy(bool copyIfEmpty) const
+{
+    const bool isEmpty = ((m_x == 0) && (m_y == 0));
+    if (isEmpty && !copyIfEmpty)
+      return {};
+    return Point(m_x, m_y);
+}
+
diff --git a/sources/shiboken2/tests/libsample/point.h b/sources/shiboken2/tests/libsample/point.h
index 579bcd515..2d9499630 100644
--- a/sources/shiboken2/tests/libsample/point.h
+++ b/sources/shiboken2/tests/libsample/point.h
@@ -31,6 +31,7 @@
 
 #include "complex.h"
 #include <utility>
+#include <optional>
 
 #include "libsamplemacros.h"
 
@@ -55,6 +56,7 @@ public:
     void midpoint(const Point& other, Point* midpoint) const;
 
     Point* copy() const;
+    std::optional<Point> copy(bool copyIfEmpty) const;
 
     inline const Point& getConstReferenceToSelf() const { return *this; }
     inline const Point* getSelf() const { return this; }
diff --git a/sources/shiboken2/tests/samplebinding/point_test.py b/sources/shiboken2/tests/samplebinding/point_test.py
index 36e6bc642..c07750107 100644
--- a/sources/shiboken2/tests/samplebinding/point_test.py
+++ b/sources/shiboken2/tests/samplebinding/point_test.py
@@ -113,5 +113,10 @@ class PointTest(unittest.TestCase):
         expected = Point((pt1.x() + pt2.x()) / 2.0, (pt1.y() + pt2.y()) / 2.0)
         self.assertEqual(pt1.midpoint(pt2), expected)
 
+    def testInvalidSignature(self):
+        pt1 = Point(0.0, 0.0)
+        pt2 = pt1.copy("string")
+        self.assertTrue(pt2 == None)
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
index 595afb9ae..50d0ff869 100644
--- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
@@ -42,6 +42,37 @@
         </conversion-rule>
     </primitive-type>
 
+    <namespace-type name='std'>
+        <value-type name='optional' generate='no'/>\n"
+    </namespace-type>
+
+    <!-- WORKAROUND to expose std::optional in a generic way -->
+    <container-type name="std::optional" type="pair">
+       <include file-name="optional" location="global"/>
+       <conversion-rule>
+           <native-to-target>
+                if (static_cast&lt;bool&gt;(%in))
+                {
+                    %INTYPE_0 value = *%in;
+                    return %CONVERTTOPYTHON[%INTYPE_0](value);
+                }
+                else
+                    Py_RETURN_NONE;
+           </native-to-target>
+           <target-to-native>
+               <add-conversion type="PyObject*">
+               if (!%in)
+                   %out = {};
+               else
+               {
+                    %OUTTYPE_0 value = %CONVERTTOCPP[%OUTTYPE_0](%in);
+                    %out = value;
+               }
+               </add-conversion>
+           </target-to-native>
+       </conversion-rule>
+    </container-type>
+
     <inject-code class="native" position="beginning">
     static bool Check2TupleOfNumbers(PyObject* pyIn) {
         if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2))
-- 
2.25.1

