diff --git a/.travis.yml b/.travis.yml index 28d3e840bb..f27211e7bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,9 @@ matrix: - sudo: true services: docker env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 + - sudo: true + services: docker + env: PYTHON=3.5 CPP=17 GCC=7 - os: osx osx_image: xcode7.3 env: PYTHON=2.7 CPP=14 CLANG @@ -46,6 +49,8 @@ matrix: - make -C docs html SPHINX_OPTIONS=-W - tools/check-style.sh - flake8 + allow_failures: + - env: PYTHON=3.5 CPP=17 GCC=7 cache: directories: - $HOME/.cache/pip @@ -56,7 +61,9 @@ before_install: if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ -z "$GCC" ]; then export GCC=4.8; fi export CXX=g++-$GCC CC=gcc-$GCC; - if [ "$GCC" = "6" ]; then export DOCKER=debian:testing CXX=g++ CC=gcc; fi + if [ "$GCC" = "6" ]; then export DOCKER=debian:testing + elif [ "$GCC" = "7" ]; then export DOCKER=debian:experimental APT_GET_EXTRA="-t experimental" + fi elif [ "$TRAVIS_OS_NAME" = "osx" ]; then export CXX=clang++ CC=clang; fi @@ -94,9 +101,10 @@ install: - | # Install dependencies if [ -n "$DOCKER" ]; then - docker exec --tty "$containerid" sh -c "for s in 0 15; do sleep \$s; apt-get -qy --no-install-recommends install \ - python$PYTHON-dev python$PY-pytest python$PY-scipy \ - libeigen3-dev cmake make g++ && break; done" + docker exec --tty "$containerid" sh -c "for s in 0 15; do sleep \$s; \ + apt-get -qy --no-install-recommends $APT_GET_EXTRA install \ + python$PY-dev python$PY-pytest python$PY-scipy \ + libeigen3-dev cmake make g++-$GCC && break; done" else pip install numpy scipy pytest diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 535516b370..6edb11041b 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -832,7 +832,6 @@ template class type_caster> { return result.release(); } -protected: std::tuple...> value; }; @@ -1220,13 +1219,15 @@ class argument_loader { using indices = make_index_sequence; public: + argument_loader() : value() {} // Helps gcc-7 properly initialize value + static constexpr auto has_kwargs = std::is_same>::value; static constexpr auto has_args = has_kwargs || std::is_same>::value; static PYBIND11_DESCR arg_names() { return detail::concat(make_caster::name()...); } - bool load_args(handle args, handle kwargs, bool convert) { - return load_impl(args, kwargs, convert, itypes{}); + bool load_args(handle args, handle kwargs) { + return load_impl(args, kwargs, itypes{}); } template @@ -1241,26 +1242,26 @@ class argument_loader { } private: - bool load_impl(handle args_, handle, bool convert, type_list) { - std::get<0>(value).load(args_, convert); + bool load_impl(handle args_, handle, type_list) { + std::get<0>(value).load(args_, true); return true; } - bool load_impl(handle args_, handle kwargs_, bool convert, type_list) { - std::get<0>(value).load(args_, convert); - std::get<1>(value).load(kwargs_, convert); + bool load_impl(handle args_, handle kwargs_, type_list) { + std::get<0>(value).load(args_, true); + std::get<1>(value).load(kwargs_, true); return true; } - bool load_impl(handle args, handle, bool convert, ... /* anything else */) { - return load_impl_sequence(args, convert, indices{}); + bool load_impl(handle args, handle, ... /* anything else */) { + return load_impl_sequence(args, indices{}); } - static constexpr bool load_impl_sequence(handle, bool, index_sequence<>) { return true; } + static constexpr bool load_impl_sequence(handle, index_sequence<>) { return true; } template - bool load_impl_sequence(handle src, bool convert, index_sequence) { - for (bool r : {std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...}) + bool load_impl_sequence(handle src, index_sequence) { + for (bool r : {std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Is), true)...}) if (!r) return false; return true; @@ -1271,7 +1272,6 @@ class argument_loader { return std::forward(f)(cast_op(std::get(value))...); } -private: std::tuple...> value; }; diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 712c1a5d62..dcb5810751 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -179,6 +179,17 @@ extern "C" { } \ PyObject *pybind11_init() +// Function return value and argument type deduction support. When compiling under C++17 these +// differ as C++17 makes the noexcept specifier part of the function type, while it is not part of +// the type under earlier standards. +#ifdef __cpp_noexcept_function_type +# define PYBIND11_NOEXCEPT_TPL_ARG , bool NoExceptions +# define PYBIND11_NOEXCEPT_SPECIFIER noexcept(NoExceptions) +#else +# define PYBIND11_NOEXCEPT_TPL_ARG +# define PYBIND11_NOEXCEPT_SPECIFIER +#endif + NAMESPACE_BEGIN(pybind11) using ssize_t = Py_ssize_t; @@ -564,16 +575,16 @@ struct nodelete { template void operator()(T*) { } }; NAMESPACE_BEGIN(detail) template struct overload_cast_impl { - template - constexpr auto operator()(Return (*pf)(Args...)) const noexcept + template + constexpr auto operator()(Return (*pf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER) const noexcept -> decltype(pf) { return pf; } - template - constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept + template + constexpr auto operator()(Return (Class::*pmf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, std::false_type = {}) const noexcept -> decltype(pmf) { return pmf; } - template - constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept + template + constexpr auto operator()(Return (Class::*pmf)(Args...) const PYBIND11_NOEXCEPT_SPECIFIER, std::true_type) const noexcept -> decltype(pmf) { return pmf; } }; NAMESPACE_END(detail) diff --git a/include/pybind11/eigen.h b/include/pybind11/eigen.h index 0a1208e16d..72e876a8ad 100644 --- a/include/pybind11/eigen.h +++ b/include/pybind11/eigen.h @@ -17,18 +17,17 @@ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# if __GNUC__ >= 7 +# pragma GCC diagnostic ignored "-Wint-in-bool-context" +# endif #endif #include #include -#if defined(__GNUG__) || defined(__clang__) -# pragma GCC diagnostic pop -#endif - #if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant #endif NAMESPACE_BEGIN(pybind11) @@ -234,6 +233,8 @@ struct type_caster::value>> { NAMESPACE_END(detail) NAMESPACE_END(pybind11) -#if defined(_MSC_VER) -#pragma warning(pop) +#if defined(__GNUG__) || defined(__clang__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) #endif diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index f1b0ebbbfe..7f1ffc1d7c 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -15,9 +15,12 @@ NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) -template struct type_caster> { - typedef std::function type; - typedef typename std::conditional::value, void_type, Return>::type retval_type; +template +struct type_caster> { + using type = std::function; + using retval_type = conditional_t::value, void_type, Return>; + using function_type = Return (*) (Args...) PYBIND11_NOEXCEPT_SPECIFIER; + public: bool load(handle src_, bool) { if (src_.is_none()) @@ -38,10 +41,9 @@ template struct type_caster(PyCFunction_GetSelf(src_.ptr())); auto rec = (function_record *) c; - using FunctionType = Return (*) (Args...); - if (rec && rec->is_stateless && rec->data[1] == &typeid(FunctionType)) { - struct capture { FunctionType f; }; + if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) { + struct capture { function_type f; }; value = ((capture *) &rec->data)->f; return true; } @@ -62,7 +64,7 @@ template struct type_caster(); + auto result = f_.template target(); if (result) return cpp_function(*result, policy).release(); else diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index e6f4efdf95..7d46f10789 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -1145,13 +1145,15 @@ template struct handle_type_name> { NAMESPACE_END(detail) -template -detail::vectorize_helper vectorize(const Func &f, Return (*) (Args ...)) { +template +detail::vectorize_helper +vectorize(const Func &f, Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) { return detail::vectorize_helper(f); } -template -detail::vectorize_helper vectorize(Return (*f) (Args ...)) { +template +detail::vectorize_helper +vectorize(Return (*f) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) { return vectorize(f, f); } diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 1db9efb8ce..1492cd290d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -44,8 +44,8 @@ class cpp_function : public function { cpp_function() { } /// Construct a cpp_function from a vanilla function pointer - template - cpp_function(Return (*f)(Args...), const Extra&... extra) { + template + cpp_function(Return (*f)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { initialize(f, f, extra...); } @@ -57,17 +57,17 @@ class cpp_function : public function { } /// Construct a cpp_function from a class method (non-const) - template - cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) { + template + cpp_function(Return (Class::*f)(Arg...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, extra...); + (Return (*) (Class *, Arg...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...); } /// Construct a cpp_function from a class method (const) - template - cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) { + template + cpp_function(Return (Class::*f)(Arg...) const PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, extra...); + (Return (*)(const Class *, Arg ...) PYBIND11_NOEXCEPT_SPECIFIER) nullptr, extra...); } /// Return the function name @@ -80,8 +80,8 @@ class cpp_function : public function { } /// Special internal constructor for functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { + template + void initialize(Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) { static_assert(detail::expected_num_args(sizeof...(Args)), "The number of named arguments does not match the function signature"); @@ -121,7 +121,7 @@ class cpp_function : public function { cast_in args_converter; /* Try to cast the function arguments into the C++ domain */ - if (!args_converter.load_args(args, kwargs, true)) + if (!args_converter.load_args(args, kwargs)) return PYBIND11_TRY_NEXT_OVERLOAD; /* Invoke call policy pre-call hook */ @@ -160,7 +160,7 @@ class cpp_function : public function { if (cast_in::has_kwargs) rec->has_kwargs = true; /* Stash some additional information used by an important optimization in 'functional.h' */ - using FunctionType = Return (*)(Args...); + using FunctionType = Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER; constexpr bool is_function_ptr = std::is_convertible::value && sizeof(capture) == sizeof(void *); diff --git a/tests/test_constants_and_functions.cpp b/tests/test_constants_and_functions.cpp index c8c0392c95..653bdf6b64 100644 --- a/tests/test_constants_and_functions.cpp +++ b/tests/test_constants_and_functions.cpp @@ -41,6 +41,26 @@ std::string print_bytes(py::bytes bytes) { return ret; } +// Test that we properly handle C++17 exception specifiers (which are part of the function signature +// in C++17). These should all still work before C++17, but don't affect the function signature. +namespace test_exc_sp { +int f1(int x) noexcept { return x+1; } +int f2(int x) noexcept(true) { return x+2; } +int f3(int x) noexcept(false) { return x+3; } +int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true) +struct C { + int m1(int x) noexcept { return x-1; } + int m2(int x) const noexcept { return x-2; } + int m3(int x) noexcept(true) { return x-3; } + int m4(int x) const noexcept(true) { return x-4; } + int m5(int x) noexcept(false) { return x-5; } + int m6(int x) const noexcept(false) { return x-6; } + int m7(int x) throw() { return x-7; } + int m8(int x) const throw() { return x-8; } +}; +} + + test_initializer constants_and_functions([](py::module &m) { m.attr("some_constant") = py::int_(14); @@ -63,4 +83,22 @@ test_initializer constants_and_functions([](py::module &m) { m.def("return_bytes", &return_bytes); m.def("print_bytes", &print_bytes); + + using namespace test_exc_sp; + py::module m2 = m.def_submodule("exc_sp"); + py::class_(m2, "C") + .def(py::init<>()) + .def("m1", &C::m1) + .def("m2", &C::m2) + .def("m3", &C::m3) + .def("m4", &C::m4) + .def("m5", &C::m5) + .def("m6", &C::m6) + .def("m7", &C::m7) + .def("m8", &C::m8) + ; + m2.def("f1", f1); + m2.def("f2", f2); + m2.def("f3", f3); + m2.def("f4", f4); }); diff --git a/tests/test_constants_and_functions.py b/tests/test_constants_and_functions.py index d13d3af1ba..2a570d2e58 100644 --- a/tests/test_constants_and_functions.py +++ b/tests/test_constants_and_functions.py @@ -22,3 +22,22 @@ def test_bytes(): from pybind11_tests import return_bytes, print_bytes assert print_bytes(return_bytes()) == "bytes[1 0 2 0]" + + +def test_exception_specifiers(): + from pybind11_tests.exc_sp import C, f1, f2, f3, f4 + + c = C() + assert c.m1(2) == 1 + assert c.m2(3) == 1 + assert c.m3(5) == 2 + assert c.m4(7) == 3 + assert c.m5(10) == 5 + assert c.m6(14) == 8 + assert c.m7(20) == 13 + assert c.m8(29) == 21 + + assert f1(33) == 34 + assert f2(53) == 55 + assert f3(86) == 89 + assert f4(140) == 144 diff --git a/tests/test_python_types.cpp b/tests/test_python_types.cpp index 33c655b520..ae77f82099 100644 --- a/tests/test_python_types.cpp +++ b/tests/test_python_types.cpp @@ -327,14 +327,14 @@ test_initializer python_types([](py::module &m) { #ifdef PYBIND11_HAS_EXP_OPTIONAL has_exp_optional = true; - using opt_int = std::experimental::optional; - m.def("double_or_zero_exp", [](const opt_int& x) -> int { + using exp_opt_int = std::experimental::optional; + m.def("double_or_zero_exp", [](const exp_opt_int& x) -> int { return x.value_or(0) * 2; }); - m.def("half_or_none_exp", [](int x) -> opt_int { - return x ? opt_int(x / 2) : opt_int(); + m.def("half_or_none_exp", [](int x) -> exp_opt_int { + return x ? exp_opt_int(x / 2) : exp_opt_int(); }); - m.def("test_nullopt_exp", [](opt_int x) { + m.def("test_nullopt_exp", [](exp_opt_int x) { return x.value_or(42); }, py::arg_v("x", std::experimental::nullopt, "None")); #endif