diff --git a/NEWS.md b/NEWS.md index 45ddf4fc..1fc6b81a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # cpp11 (development version) +* cpp11 is now able to compile on gcc 4.8.5 (#69, @bkietz) + * `as_cpp()` now allows enumeration `E` (#52, @bkietz) * `writable::logicals::operator=()` now allows C++ boolean values (#57, @romainfrancois) diff --git a/inst/include/cpp11/protect.hpp b/inst/include/cpp11/protect.hpp index e7683be9..c6405cd2 100644 --- a/inst/include/cpp11/protect.hpp +++ b/inst/include/cpp11/protect.hpp @@ -6,6 +6,7 @@ #include // for exception #include // for std::runtime_error #include // for string, basic_string +#include // for tuple, make_tuple #include "R_ext/Error.h" // for Rf_error, Rf_warning #include "R_ext/Print.h" // for REprintf #include "R_ext/Utils.h" // for R_CheckUserInterrupt @@ -177,12 +178,57 @@ void unwind_protect(Fun code) { } #endif +namespace detail { + +template +struct index_sequence { + using type = index_sequence; +}; + +template +struct appended_sequence; + +template +struct appended_sequence, J> : index_sequence {}; + +template +struct make_index_sequence + : appended_sequence::type, N - 1> {}; + +template <> +struct make_index_sequence<0> : index_sequence<> {}; + +template +auto apply(F&& f, std::tuple&& a, const index_sequence&) + -> decltype(f(std::get(std::move(a))...)) { + return f(std::get(std::move(a))...); +} + +template +auto apply(F&& f, std::tuple&& a) + -> decltype(apply(f, std::move(a), make_index_sequence{})) { + return apply(f, std::move(a), make_index_sequence{}); +} + +// overload to silence a compiler warning that the tuple parameter is set but unused +template +auto apply(F&& f, std::tuple<> &&) -> decltype(f()) { + return f(); +} + +} // namespace detail + struct protect { template struct function { template - auto operator()(A... a) const -> decltype(std::declval()(a...)) { - return unwind_protect([&] { return ptr_(a...); }); + auto operator()(A&&... a) const + -> decltype(detail::apply(std::declval(), + std::forward_as_tuple(std::forward(a)...))) { + // workaround to support gcc4.8, which can't capture a parameter pack + auto a_packed_refs = std::forward_as_tuple(std::forward(a)...); + return unwind_protect( + [&] { return detail::apply(ptr_, std::move(a_packed_refs)); }); } F* ptr_; }; @@ -198,7 +244,7 @@ inline void check_user_interrupt() { safe[R_CheckUserInterrupt](); } template void stop [[noreturn]] (const char* fmt, Args... args) { - unwind_protect([&] { Rf_error(fmt, args...); }); + safe[Rf_error](fmt, args...); // Compiler hint to allow [[noreturn]] attribute; this is never executed since Rf_error // will longjmp throw std::runtime_error("stop()"); @@ -206,7 +252,7 @@ void stop [[noreturn]] (const char* fmt, Args... args) { template void stop [[noreturn]] (const std::string& fmt, Args... args) { - unwind_protect([&] { Rf_error(fmt.c_str(), args...); }); + safe[Rf_error](fmt.c_str(), args...); // Compiler hint to allow [[noreturn]] attribute; this is never executed since Rf_error // will longjmp throw std::runtime_error("stop()");