From cdd75d4e7f0402a0537c516b7331a036347b0fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= Date: Fri, 31 Jan 2020 12:01:39 +0900 Subject: [PATCH] support C++ std::nullptr_t C++ keyword `nullptr` represents a null pointer (note also that NULL is an integer in C++ due to its design flaw). Its type is `std::nullptr_t`, defined in standard header. Why not support it when the backend implementation can take a null pointer as an argument. --- configure.ac | 11 ++ ext/-test-/cxxanyargs/cxxanyargs.cpp | 122 ++++++++++++++++++++ include/ruby/backward/cxxanyargs.hpp | 166 ++++++++++++++++++++++++++- win32/Makefile.sub | 3 + 4 files changed, 301 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 548849d612..b0e09a5f85 100644 --- a/configure.ac +++ b/configure.ac @@ -1437,6 +1437,17 @@ AS_IF([test "$rb_cv_CentOS6_CXX_workaround" != no],[ AC_DEFINE([RUBY_CXX_DEPRECATED(msg)], [__attribute__((__deprecated__(msg)))])]) +AC_CACHE_CHECK([for std::nullptr_t], rb_cv_CXX_nullptr, [ + AC_LANG_PUSH([C++]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [@%:@include ], + [static std::nullptr_t const *const conftest = nullptr;])], + [rb_cv_CXX_nullptr=yes], + [rb_cv_CXX_nullptr=no]) + AC_LANG_POP()]) +AS_IF([test "$rb_cv_CXX_nullptr" != no],[AC_DEFINE(HAVE_NULLPTR)]) + if_i386=${universal_binary+[defined __i386__]} RUBY_FUNC_ATTRIBUTE(__stdcall__, FUNC_STDCALL, rb_cv_func_stdcall, ${if_i386}) RUBY_FUNC_ATTRIBUTE(__cdecl__, FUNC_CDECL, rb_cv_func_cdecl, ${if_i386}) diff --git a/ext/-test-/cxxanyargs/cxxanyargs.cpp b/ext/-test-/cxxanyargs/cxxanyargs.cpp index 812c6d7e60..b0d48e0478 100644 --- a/ext/-test-/cxxanyargs/cxxanyargs.cpp +++ b/ext/-test-/cxxanyargs/cxxanyargs.cpp @@ -36,6 +36,18 @@ namespace test_rb_define_virtual_variable { RUBY_METHOD_FUNC(getter), reinterpret_cast(setter)); // old rb_define_virtual_variable("test", getter, setter); // new + +#ifdef HAVE_NULLPTR + rb_define_virtual_variable("test", nullptr, reinterpret_cast(setter)); + rb_define_virtual_variable("test", nullptr, setter); + + rb_define_virtual_variable("test", RUBY_METHOD_FUNC(getter), nullptr); + rb_define_virtual_variable("test", getter, nullptr); + + // It doesn't make any sense for both function pointers be nullptr at + // the same time. +#endif + return self; } } @@ -62,6 +74,18 @@ struct test_rb_define_hooked_variable { RUBY_METHOD_FUNC(getter), reinterpret_cast(setter)); // old rb_define_hooked_variable("test", &v, getter, setter); // new + +#ifdef HAVE_NULLPTR + rb_define_hooked_variable("test", &v, nullptr, reinterpret_cast(setter)); + rb_define_hooked_variable("test", &v, nullptr, setter); + + rb_define_hooked_variable("test", &v, RUBY_METHOD_FUNC(getter), nullptr); + rb_define_hooked_variable("test", &v, getter, nullptr); + + // It doesn't make any sense for both function pointers be nullptr at + // the same time. +#endif + return self; } }; @@ -83,6 +107,10 @@ namespace test_rb_iterate { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + rb_iterate(iter, self, nullptr, self); +#endif + rb_iterate(iter, self, RUBY_METHOD_FUNC(block), self); // old return rb_iterate(iter, self, block, self); // new } @@ -100,6 +128,11 @@ namespace test_rb_block_call { { const ID mid = rb_intern("each"); const VALUE argv[] = { Qundef }; + +#ifdef HAVE_NULLPTR + rb_block_call(self, mid, 0, argv, nullptr, self); +#endif + rb_block_call(self, mid, 0, argv, RUBY_METHOD_FUNC(block), self); // old return rb_block_call(self, mid, 0, argv, block, self); // new } @@ -121,6 +154,11 @@ namespace test_rb_rescue { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + rb_rescue(RUBY_METHOD_FUNC(begin), self, nullptr, self); + return rb_rescue(begin, self, nullptr, self); +#endif + rb_rescue(RUBY_METHOD_FUNC(begin), self, RUBY_METHOD_FUNC(rescue), self); // old return rb_rescue(begin, self, rescue, self); // new } @@ -142,6 +180,11 @@ namespace test_rb_rescue2 { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + rb_rescue2(RUBY_METHOD_FUNC(begin), self, nullptr, self, rb_eStandardError, rb_eFatal, 0); + rb_rescue2(begin, self, nullptr, self, rb_eStandardError, rb_eFatal, 0); +#endif + rb_rescue2(RUBY_METHOD_FUNC(begin), self, RUBY_METHOD_FUNC(rescue), self, rb_eStandardError, rb_eFatal, 0); // old return rb_rescue2(begin, self, rescue, self, rb_eStandardError, rb_eFatal, 0); // new @@ -164,6 +207,11 @@ namespace test_rb_ensure { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + rb_ensure(RUBY_METHOD_FUNC(begin), self, nullptr, self); + rb_ensure(begin, self, nullptr, self); +#endif + rb_ensure(RUBY_METHOD_FUNC(begin), self, RUBY_METHOD_FUNC(ensure), self); // old return rb_ensure(begin, self, ensure, self); // new } @@ -180,6 +228,11 @@ namespace test_rb_catch { test(VALUE self) { static const char *zero = 0; + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a catcher. +#endif + rb_catch(zero, RUBY_METHOD_FUNC(catcher), self); // old return rb_catch(zero, catcher, self); // new } @@ -195,6 +248,10 @@ namespace test_rb_catch_obj { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a catcher. +#endif + rb_catch_obj(self, RUBY_METHOD_FUNC(catcher), self); // old return rb_catch_obj(self, catcher, self); // new } @@ -210,6 +267,10 @@ namespace test_rb_fiber_new { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a fiber. +#endif + rb_fiber_new(RUBY_METHOD_FUNC(fiber), self); // old return rb_fiber_new(fiber, self); // new } @@ -225,6 +286,10 @@ namespace test_rb_proc_new { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a proc. +#endif + rb_fiber_new(RUBY_METHOD_FUNC(proc), self); // old return rb_fiber_new(proc, self); // new } @@ -244,6 +309,11 @@ struct test_rb_thread_create { test(VALUE self) { v = self; + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a thread. +#endif + rb_thread_create(RUBY_METHOD_FUNC(thread), &v); // old return rb_thread_create(thread, &v); // new } @@ -262,6 +332,11 @@ namespace test_st_foreach { { st_data_t data = 0; st_table *st = st_init_numtable(); + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as an iterator. +#endif + st_foreach(st, reinterpret_cast(iter), data); // old st_foreach(st, iter, data); // new return self; @@ -280,6 +355,11 @@ namespace test_st_foreach_check { { st_data_t data = 0; st_table *st = st_init_numtable(); + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as an iterator. +#endif + st_foreach_check(st, reinterpret_cast(iter), data, data); // old st_foreach_check(st, iter, data, data); // new return self; @@ -298,6 +378,11 @@ namespace test_st_foreach_safe { { st_data_t data = 0; st_table *st = st_init_numtable(); + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as an iterator. +#endif + st_foreach_safe(st, reinterpret_cast(iter), data); // old st_foreach_safe(st, iter, data); // new return self; @@ -315,6 +400,11 @@ namespace test_rb_hash_foreach { test(VALUE self) { VALUE h = rb_hash_new(); + +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as an iterator. +#endif + rb_hash_foreach(h, reinterpret_cast(iter), self); // old rb_hash_foreach(h, iter, self); // new return self; @@ -331,6 +421,10 @@ namespace test_rb_ivar_foreach { VALUE test(VALUE self) { +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as an iterator. +#endif + rb_ivar_foreach(self, reinterpret_cast(iter), self); // old rb_ivar_foreach(self, iter, self); // new return self; @@ -399,6 +493,10 @@ namespace test_rb_define_method { rb_define_method(self, "mv", rb_f_notimplement, -1); rb_define_method(self, "mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -465,6 +563,10 @@ namespace test_rb_define_method_id { rb_define_method_id(self, rb_intern("mv"), rb_f_notimplement, -1); rb_define_method_id(self, rb_intern("mc"), rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -531,6 +633,10 @@ namespace test_rb_define_module_function { rb_define_module_function(self, "mv", rb_f_notimplement, -1); rb_define_module_function(self, "mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -597,6 +703,10 @@ namespace test_rb_define_singleton_method { rb_define_singleton_method(self, "mv", rb_f_notimplement, -1); rb_define_singleton_method(self, "mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -663,6 +773,10 @@ namespace test_rb_define_protected_method { rb_define_protected_method(self, "mv", rb_f_notimplement, -1); rb_define_protected_method(self, "mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -729,6 +843,10 @@ namespace test_rb_define_private_method { rb_define_private_method(self, "mv", rb_f_notimplement, -1); rb_define_private_method(self, "mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } @@ -795,6 +913,10 @@ namespace test_rb_define_global_function { rb_define_global_function("mv", rb_f_notimplement, -1); rb_define_global_function("mc", rb_f_notimplement, -1); +#ifdef HAVE_NULLPTR + // It doesn't make any sense at all to pass nullptr as a method. +#endif + return self; } } diff --git a/include/ruby/backward/cxxanyargs.hpp b/include/ruby/backward/cxxanyargs.hpp index 431d719dde..132956ec3d 100644 --- a/include/ruby/backward/cxxanyargs.hpp +++ b/include/ruby/backward/cxxanyargs.hpp @@ -13,6 +13,10 @@ extern "C++" { +#ifdef HAVE_NULLPTR +#include +#endif + /// @brief The main namespace. /// @note The name "ruby" might already be taken, but that must not be a /// problem because namespaces are allowed to reopen. @@ -46,6 +50,9 @@ typedef void void_type(ANYARGS); /// @brief ANYARGS-ed function type, int variant. typedef int int_type(ANYARGS); +/// @brief single-argumented function type. +typedef VALUE onearg_type(VALUE); + /// @name Hooking global variables /// @{ @@ -95,6 +102,58 @@ rb_define_virtual_variable(const char *q, type *w, rb_gvar_setter_t *e) ::rb_define_virtual_variable(q, r, e); } +#ifdef HAVE_NULLPTR +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Getter function. +/// @param[in] e Setter function. +/// @see rb_define_hooked_variable() +inline void +rb_define_virtual_variable(const char *q, rb_gvar_getter_t *w, std::nullptr_t e) +{ + ::rb_define_virtual_variable(q, w, e); +} + +RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Getter function. +/// @param[in] e Setter function. +/// @see rb_define_hooked_variable() +/// @deprecated Use glanular typed overload instead. +inline void +rb_define_virtual_variable(const char *q, type *w, std::nullptr_t e) +{ + rb_gvar_getter_t *r = reinterpret_cast(w); + ::rb_define_virtual_variable(q, r, e); +} + +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Getter function. +/// @param[in] e Setter function. +/// @see rb_define_hooked_variable() +inline void +rb_define_virtual_variable(const char *q, std::nullptr_t w, rb_gvar_setter_t *e) +{ + ::rb_define_virtual_variable(q, w, e); +} + +RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Getter function. +/// @param[in] e Setter function. +/// @see rb_define_hooked_variable() +/// @deprecated Use glanular typed overload instead. +inline void +rb_define_virtual_variable(const char *q, std::nullptr_t w, void_type *e) +{ + rb_gvar_setter_t *r = reinterpret_cast(e); + ::rb_define_virtual_variable(q, w, r); +} +#endif + RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @brief Define a function-backended global variable. /// @param[in] q Name of the variable. @@ -144,6 +203,62 @@ rb_define_hooked_variable(const char *q, VALUE *w, type *e, rb_gvar_setter_t *r) ::rb_define_hooked_variable(q, w, t, r); } +#ifdef HAVE_NULLPTR +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Variable storage. +/// @param[in] e Getter function. +/// @param[in] r Setter function. +/// @see rb_define_virtual_variable() +inline void +rb_define_hooked_variable(const char *q, VALUE *w, rb_gvar_getter_t *e, std::nullptr_t r) +{ + ::rb_define_hooked_variable(q, w, e, r); +} + +RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Variable storage. +/// @param[in] e Getter function. +/// @param[in] r Setter function. +/// @see rb_define_virtual_variable() +/// @deprecated Use glanular typed overload instead. +inline void +rb_define_hooked_variable(const char *q, VALUE *w, type *e, std::nullptr_t r) +{ + rb_gvar_getter_t *y = reinterpret_cast(e); + ::rb_define_hooked_variable(q, w, y, r); +} + +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Variable storage. +/// @param[in] e Getter function. +/// @param[in] r Setter function. +/// @see rb_define_virtual_variable() +inline void +rb_define_hooked_variable(const char *q, VALUE *w, std::nullptr_t e, rb_gvar_setter_t *r) +{ + ::rb_define_hooked_variable(q, w, e, r); +} + +RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") +/// @brief Define a function-backended global variable. +/// @param[in] q Name of the variable. +/// @param[in] w Variable storage. +/// @param[in] e Getter function. +/// @param[in] r Setter function. +/// @see rb_define_virtual_variable() +/// @deprecated Use glanular typed overload instead. +inline void +rb_define_hooked_variable(const char *q, VALUE *w, std::nullptr_t e, void_type *r) +{ + rb_gvar_setter_t *y = reinterpret_cast(r); + ::rb_define_hooked_variable(q, w, e, y); +} +#endif + /// @} /// @name Exceptions and tag jumps /// @{ @@ -159,12 +274,28 @@ RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @deprecated This function is obsolated since long before 2.x era. Do not /// use it any longer. rb_block_call() is provided instead. inline VALUE -rb_iterate(VALUE(*q)(VALUE), VALUE w, type *e, VALUE r) +rb_iterate(onearg_type *q, VALUE w, type *e, VALUE r) { rb_block_call_func_t t = reinterpret_cast(e); return ::rb_iterate(q, w, t, r); } +#ifdef HAVE_NULLPTR +/// @brief Old way to implement iterators. +/// @param[in] q A function that can yield. +/// @param[in] w Passed to `q`. +/// @param[in] e What is to be yielded. +/// @param[in] r Passed to `e`. +/// @return The return value of `q`. +/// @deprecated This function is obsolated since long before 2.x era. Do not +/// use it any longer. rb_block_call() is provided instead. +inline VALUE +rb_iterate(onearg_type *q, VALUE w, std::nullptr_t e, VALUE r) +{ + return ::rb_iterate(q, w, e, r); +} +#endif + RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @brief Call a method with a block. /// @param[in] q The self. @@ -183,6 +314,22 @@ rb_block_call(VALUE q, ID w, int e, const VALUE *r, type *t, VALUE y) return ::rb_block_call(q, w, e, r, u, y); } +#ifdef HAVE_NULLPTR +/// @brief Call a method with a block. +/// @param[in] q The self. +/// @param[in] w The method. +/// @param[in] e The # of elems of `r` +/// @param[in] r The arguments. +/// @param[in] t What is to be yielded. +/// @param[in] y Passed to `t` +/// @return Return value of `q#w(*r,&t)` +inline VALUE +rb_block_call(VALUE q, ID w, int e, const VALUE *r, std::nullptr_t t, VALUE y) +{ + return ::rb_block_call(q, w, e, r, t, y); +} +#endif + RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @brief An equivalent of `rescue` clause. /// @param[in] q A function that can raise. @@ -274,6 +421,23 @@ rb_catch(const char *q, type *w, VALUE e) return ::rb_catch(q, r, e); } +#ifdef HAVE_NULLPTR +/// @brief An equivalent of `Kernel#catch`. +/// @param[in] q The "tag" string. +/// @param[in] w A function that can throw. +/// @param[in] e Passed to `w`. +/// @return What was thrown. +/// @see rb_block_call() +/// @see rb_protect() +/// @see rb_rb_catch_obj() +/// @see rb_rescue() +inline VALUE +rb_catch(const char *q, std::nullptr_t w, VALUE e) +{ + return ::rb_catch(q, w, e); +} +#endif + RUBY_CXX_DEPRECATED("Use of ANYARGS in this function is deprecated") /// @brief An equivalent of `Kernel#catch`. /// @param[in] q The "tag" object. diff --git a/win32/Makefile.sub b/win32/Makefile.sub index 326bd7fa7a..ea5789fd3e 100644 --- a/win32/Makefile.sub +++ b/win32/Makefile.sub @@ -851,6 +851,9 @@ $(CONFIG_H): $(MKFILES) $(srcdir)/win32/Makefile.sub $(win_srcdir)/Makefile.sub #define HAVE_QSORT_S !endif #define HAVE_TYPE_NET_LUID 1 +!if $(MSC_VER) >= 1600 +#define HAVE_NULLPTR 1 +!endif #define SETPGRP_VOID 1 #define RSHIFT(x,y) ((x)>>(int)y) #define HAVE_RB_FD_INIT 1