Skip to content

Various C++17 fixes #555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
28 changes: 14 additions & 14 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,6 @@ template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
return result.release();
}

protected:
std::tuple<make_caster<Tuple>...> value;
};

Expand Down Expand Up @@ -1220,13 +1219,15 @@ class argument_loader {
using indices = make_index_sequence<sizeof...(Args)>;

public:
argument_loader() : value() {} // Helps gcc-7 properly initialize value

static constexpr auto has_kwargs = std::is_same<itypes, type_list<args, kwargs>>::value;
static constexpr auto has_args = has_kwargs || std::is_same<itypes, type_list<args>>::value;

static PYBIND11_DESCR arg_names() { return detail::concat(make_caster<Args>::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 <typename Return, typename Func>
Expand All @@ -1241,26 +1242,26 @@ class argument_loader {
}

private:
bool load_impl(handle args_, handle, bool convert, type_list<args>) {
std::get<0>(value).load(args_, convert);
bool load_impl(handle args_, handle, type_list<args>) {
std::get<0>(value).load(args_, true);
return true;
}

bool load_impl(handle args_, handle kwargs_, bool convert, type_list<args, kwargs>) {
std::get<0>(value).load(args_, convert);
std::get<1>(value).load(kwargs_, convert);
bool load_impl(handle args_, handle kwargs_, type_list<args, kwargs>) {
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 <size_t... Is>
bool load_impl_sequence(handle src, bool convert, index_sequence<Is...>) {
for (bool r : {std::get<Is>(value).load(PyTuple_GET_ITEM(src.ptr(), Is), convert)...})
bool load_impl_sequence(handle src, index_sequence<Is...>) {
for (bool r : {std::get<Is>(value).load(PyTuple_GET_ITEM(src.ptr(), Is), true)...})
if (!r)
return false;
return true;
Expand All @@ -1271,7 +1272,6 @@ class argument_loader {
return std::forward<Func>(f)(cast_op<Args>(std::get<Is>(value))...);
}

private:
std::tuple<make_caster<Args>...> value;
};

Expand Down
23 changes: 17 additions & 6 deletions include/pybind11/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -564,16 +575,16 @@ struct nodelete { template <typename T> void operator()(T*) { } };
NAMESPACE_BEGIN(detail)
template <typename... Args>
struct overload_cast_impl {
template <typename Return>
constexpr auto operator()(Return (*pf)(Args...)) const noexcept
template <typename Return /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
constexpr auto operator()(Return (*pf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER) const noexcept
-> decltype(pf) { return pf; }

template <typename Return, typename Class>
constexpr auto operator()(Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
constexpr auto operator()(Return (Class::*pmf)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, std::false_type = {}) const noexcept
-> decltype(pmf) { return pmf; }

template <typename Return, typename Class>
constexpr auto operator()(Return (Class::*pmf)(Args...) const, std::true_type) const noexcept
template <typename Return, typename Class /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
constexpr auto operator()(Return (Class::*pmf)(Args...) const PYBIND11_NOEXCEPT_SPECIFIER, std::true_type) const noexcept
-> decltype(pmf) { return pmf; }
};
NAMESPACE_END(detail)
Expand Down
17 changes: 9 additions & 8 deletions include/pybind11/eigen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Eigen/Core>
#include <Eigen/SparseCore>

#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)
Expand Down Expand Up @@ -234,6 +233,8 @@ struct type_caster<Type, enable_if_t<is_eigen_sparse<Type>::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
16 changes: 9 additions & 7 deletions include/pybind11/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@
NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)

template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> {
typedef std::function<Return(Args...)> type;
typedef typename std::conditional<std::is_same<Return, void>::value, void_type, Return>::type retval_type;
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
struct type_caster<std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>> {
using type = std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>;
using retval_type = conditional_t<std::is_same<Return, void>::value, void_type, Return>;
using function_type = Return (*) (Args...) PYBIND11_NOEXCEPT_SPECIFIER;

public:
bool load(handle src_, bool) {
if (src_.is_none())
Expand All @@ -38,10 +41,9 @@ template <typename Return, typename... Args> struct type_caster<std::function<Re
if (PyCFunction_Check(src_.ptr())) {
auto c = reinterpret_borrow<capsule>(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;
}
Expand All @@ -62,7 +64,7 @@ template <typename Return, typename... Args> struct type_caster<std::function<Re
if (!f_)
return none().inc_ref();

auto result = f_.template target<Return (*)(Args...)>();
auto result = f_.template target<function_type>();
if (result)
return cpp_function(*result, policy).release();
else
Expand Down
10 changes: 6 additions & 4 deletions include/pybind11/numpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -1145,13 +1145,15 @@ template <typename T, int Flags> struct handle_type_name<array_t<T, Flags>> {

NAMESPACE_END(detail)

template <typename Func, typename Return, typename... Args>
detail::vectorize_helper<Func, Return, Args...> vectorize(const Func &f, Return (*) (Args ...)) {
template <typename Func, typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
detail::vectorize_helper<Func, Return, Args...>
vectorize(const Func &f, Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
return detail::vectorize_helper<Func, Return, Args...>(f);
}

template <typename Return, typename... Args>
detail::vectorize_helper<Return (*) (Args ...), Return, Args...> vectorize(Return (*f) (Args ...)) {
template <typename Return, typename... Args /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
detail::vectorize_helper<Return (*) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER, Return, Args...>
vectorize(Return (*f) (Args ...) PYBIND11_NOEXCEPT_SPECIFIER) {
return vectorize<Return (*) (Args ...), Return, Args...>(f, f);
}

Expand Down
24 changes: 12 additions & 12 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ class cpp_function : public function {
cpp_function() { }

/// Construct a cpp_function from a vanilla function pointer
template <typename Return, typename... Args, typename... Extra>
cpp_function(Return (*f)(Args...), const Extra&... extra) {
template <typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
cpp_function(Return (*f)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
initialize(f, f, extra...);
}

Expand All @@ -57,17 +57,17 @@ class cpp_function : public function {
}

/// Construct a cpp_function from a class method (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...), const Extra&... extra) {
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
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 <typename Return, typename Class, typename... Arg, typename... Extra>
cpp_function(Return (Class::*f)(Arg...) const, const Extra&... extra) {
template <typename Return, typename Class, typename... Arg, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
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
Expand All @@ -80,8 +80,8 @@ class cpp_function : public function {
}

/// Special internal constructor for functors, lambda functions, etc.
template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
template <typename Func, typename Return, typename... Args, typename... Extra /*,*/ PYBIND11_NOEXCEPT_TPL_ARG>
void initialize(Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
static_assert(detail::expected_num_args<Extra...>(sizeof...(Args)),
"The number of named arguments does not match the function signature");

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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<Func, FunctionType>::value &&
sizeof(capture) == sizeof(void *);
Expand Down
38 changes: 38 additions & 0 deletions tests/test_constants_and_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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_<C>(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);
});
19 changes: 19 additions & 0 deletions tests/test_constants_and_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading