diff --git a/.appveyor.yml b/.appveyor.yml index 8d30cf31a..b66b8e549 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -28,8 +28,12 @@ matrix: install: - ps: | if ($env:PLATFORM -eq "x64") { $env:CMAKE_ARCH = "x64" } - if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { $env:CMAKE_GENERATOR = "Visual Studio 15 2017" } - else { $env:CMAKE_GENERATOR = "Visual Studio 14 2015" } + if ($env:APPVEYOR_JOB_NAME -like "*Visual Studio 2017*") { + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" + $env:CMAKE_INCLUDE_PATH = "C:\Libraries\boost_1_64_0" + } else { + $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + } if ($env:PYTHON) { if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" @@ -45,7 +49,7 @@ install: - ps: | Start-FileDownload 'http://bitbucket.org/eigen/eigen/get/3.3.3.zip' 7z x 3.3.3.zip -y > $null - $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f" + $env:CMAKE_INCLUDE_PATH = "eigen-eigen-67e894c6cd8f;$env:CMAKE_INCLUDE_PATH" build_script: - cmake -G "%CMAKE_GENERATOR%" -A "%CMAKE_ARCH%" -DPYBIND11_CPP_STANDARD=/std:c++%CPP% diff --git a/.travis.yml b/.travis.yml index f6ee9af0d..ae64317ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -150,7 +150,7 @@ install: $SCRIPT_RUN_PREFIX sh -c "for s in 0 15; do sleep \$s; \ apt-get -qy --no-install-recommends install \ $PY_DEBUG python$PYTHON-dev python$PY-pytest python$PY-scipy \ - libeigen3-dev cmake make ${COMPILER_PACKAGES} && break; done" + libeigen3-dev libboost-dev cmake make ${COMPILER_PACKAGES} && break; done" else if [ "$CLANG" = "4.0" ]; then diff --git a/docs/advanced/cast/stl.rst b/docs/advanced/cast/stl.rst index ac6318f0e..2d709d7cd 100644 --- a/docs/advanced/cast/stl.rst +++ b/docs/advanced/cast/stl.rst @@ -60,8 +60,7 @@ for custom variant types: template <> struct visit_helper { template - static auto call(Args &&...args) - -> decltype(boost::apply_visitor(args...)) { + static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) { return boost::apply_visitor(args...); } }; @@ -71,6 +70,13 @@ The ``visit_helper`` specialization is not required if your ``name::variant`` pr a ``name::visit()`` function. For any other function name, the specialization must be included to tell pybind11 how to visit the variant. +.. note:: + + pybind11 only supports the modern implementation of ``boost::variant`` + which makes use of variadic templates. This requires Boost 1.56 or newer. + Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` + falls back to the old non-variadic implementation on MSVC 2015. + .. _opaque: Making opaque types diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index d07a81f98..c8c9e9060 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -292,8 +292,10 @@ struct variant_caster_visitor { return_value_policy policy; handle parent; + using result_type = handle; // required by boost::variant in C++11 + template - handle operator()(T &&src) const { + result_type operator()(T &&src) const { return make_caster::cast(std::forward(src), policy, parent); } }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index aa2704b29..18e6b9f24 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -112,6 +112,9 @@ if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) endif() endif() +# Optional dependency for some tests +find_package(Boost) + # Compile with compiler warnings turned on function(pybind11_enable_warnings target_name) if(MSVC) @@ -141,37 +144,40 @@ foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) endforeach() set(testdir ${CMAKE_CURRENT_SOURCE_DIR}) -foreach(tgt ${test_targets}) +foreach(target ${test_targets}) set(test_files ${PYBIND11_TEST_FILES}) - if(NOT tgt STREQUAL "pybind11_tests") + if(NOT target STREQUAL "pybind11_tests") set(test_files "") endif() # Create the binding library - pybind11_add_module(${tgt} THIN_LTO ${tgt}.cpp - ${test_files} ${PYBIND11_HEADERS}) - - pybind11_enable_warnings(${tgt}) + pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS}) + pybind11_enable_warnings(${target}) if(MSVC) - target_compile_options(${tgt} PRIVATE /utf-8) + target_compile_options(${target} PRIVATE /utf-8) endif() if(EIGEN3_FOUND) if (PYBIND11_EIGEN_VIA_TARGET) - target_link_libraries(${tgt} PRIVATE Eigen3::Eigen) + target_link_libraries(${target} PRIVATE Eigen3::Eigen) else() - target_include_directories(${tgt} PRIVATE ${EIGEN3_INCLUDE_DIR}) + target_include_directories(${target} PRIVATE ${EIGEN3_INCLUDE_DIR}) endif() - target_compile_definitions(${tgt} PRIVATE -DPYBIND11_TEST_EIGEN) + target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN) + endif() + + if(Boost_FOUND) + target_include_directories(${target} PRIVATE ${Boost_INCLUDE_DIRS}) + target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) endif() # Always write the output file directly into the 'tests' directory (even on MSVC) if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) - set_target_properties(${tgt} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir}) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${testdir}) foreach(config ${CMAKE_CONFIGURATION_TYPES}) string(TOUPPER ${config} config) - set_target_properties(${tgt} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir}) + set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} ${testdir}) endforeach() endif() endforeach() diff --git a/tests/test_stl.cpp b/tests/test_stl.cpp index 93e8c665f..66fe9d1ce 100644 --- a/tests/test_stl.cpp +++ b/tests/test_stl.cpp @@ -10,6 +10,27 @@ #include "pybind11_tests.h" #include +// Test with `std::variant` in C++17 mode, or with `boost::variant` in C++11/14 +#if PYBIND11_HAS_VARIANT +using std::variant; +#elif PYBIND11_TEST_BOOST +# include +# define PYBIND11_HAS_VARIANT 1 +using boost::variant; + +namespace pybind11 { namespace detail { +template +struct type_caster> : variant_caster> {}; + +template <> +struct visit_helper { + template + static auto call(Args &&...args) -> decltype(boost::apply_visitor(args...)) { + return boost::apply_visitor(args...); + } +}; +}} // namespace pybind11::detail +#endif /// Issue #528: templated constructor struct TplCtorClass { @@ -162,22 +183,27 @@ TEST_SUBMODULE(stl, m) { #endif #ifdef PYBIND11_HAS_VARIANT + static_assert(std::is_same::value, + "visitor::result_type is required by boost::variant in C++11 mode"); + struct visitor { - const char *operator()(int) { return "int"; } - const char *operator()(std::string) { return "std::string"; } - const char *operator()(double) { return "double"; } - const char *operator()(std::nullptr_t) { return "std::nullptr_t"; } + using result_type = const char *; + + result_type operator()(int) { return "int"; } + result_type operator()(std::string) { return "std::string"; } + result_type operator()(double) { return "double"; } + result_type operator()(std::nullptr_t) { return "std::nullptr_t"; } }; // test_variant - m.def("load_variant", [](std::variant v) { - return std::visit(visitor(), v); + m.def("load_variant", [](variant v) { + return py::detail::visit_helper::call(visitor(), v); }); - m.def("load_variant_2pass", [](std::variant v) { - return std::visit(visitor(), v); + m.def("load_variant_2pass", [](variant v) { + return py::detail::visit_helper::call(visitor(), v); }); m.def("cast_variant", []() { - using V = std::variant; + using V = variant; return py::make_tuple(V(5), V("Hello")); }); #endif