diff options
author | 卜部昌平 <shyouhei@ruby-lang.org> | 2020-01-28 11:43:33 +0900 |
---|---|---|
committer | 卜部昌平 <shyouhei@ruby-lang.org> | 2020-01-28 15:42:57 +0900 |
commit | 01825e8bffde9f4517e60878f8a829f91c361d68 (patch) | |
tree | 2502a1b380f618a788079b8a0774dab0d3ef7843 | |
parent | ab33b3d6915fe40734cdeaac5f2104fa8792c8a9 (diff) | |
download | ruby-01825e8bffde9f4517e60878f8a829f91c361d68.tar.gz |
template metaprogramming instead of macros
C++ (and myself) hates macros. If we could do the same thing in both
preprocessor and template, we shall choose template. This particular
part of the ruby header is one of such situations.
-rw-r--r-- | include/ruby/backward/cxxanyargs.hpp | 186 | ||||
-rw-r--r-- | include/ruby/ruby.h | 12 |
2 files changed, 189 insertions, 9 deletions
diff --git a/include/ruby/backward/cxxanyargs.hpp b/include/ruby/backward/cxxanyargs.hpp index 3585f678b8..b2491956d7 100644 --- a/include/ruby/backward/cxxanyargs.hpp +++ b/include/ruby/backward/cxxanyargs.hpp @@ -11,6 +11,8 @@ /// meant to be a backwards compatibility shim. Please stick to /// C++ 98 and never use newer features, like `constexpr`. +extern "C++" { + /// @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. @@ -433,7 +435,189 @@ rb_ivar_foreach(VALUE q, int_type *w, VALUE e) } /// @} -}}} + +/// @brief Driver for *_define_method +/// +/// ::rb_define_method function for instance takes a pointer to ANYARGS-ed +/// functions, which in fact varies 18 different prototypes. We still need to +/// preserve ANYARGS for storages but why not check the consistencies if +/// possible. In C++ a function has its own prototype, which is a compile-time +/// constant (static type) by nature. We can list up all the possible input +/// types and provide warnings for other cases. This is such attempt. +namespace define_method { + +/// @brief Template metaprogramming to generate function prototypes. +/// @tparam T Type of method id (`ID` or `const char*` in practice). +/// @tparam F Definition driver e.g. ::rb_define_method. +template<typename T, void (*F)(VALUE klass, T mid, type *func, int arity)> +struct driver { + + /// @brief Defines a method + /// @tparam N Arity of the function. + /// @tparam U The function in question + template<int N, typename U> + struct engine { + + /* :TODO: Following deprecation attribute renders tons of warnings (one + * per every method definitions), which is annoying. Of course + * annoyance is the core feature of deprecation warnings... But that + * could be too much, especially when the warnings happen inside of + * machine-generated programs. And SWIG is known to do such thing. + * The new (granular) API was introduced in API version 2.7. As of + * this writing the version is 2.8. Let's warn this later, some time + * during 3.x. Hopefully codes in old (ANYARGS-ed) format should be + * less than now. */ +#if (RUBY_API_VERSION_MAJOR * 100 + RUBY_API_VERSION_MINOR) >= 301 + RUBY_CXX_DEPRECATED("use of ANYARGS is deprecated") +#endif + /// @brief Defines klass#mid as func, whose arity is N. + /// @param[in] klass Where the method lives. + /// @param[in] mid Name of the method to define. + /// @param[in] func Function that implements klass#mid. + /// @deprecated Pass corrctly typed function instead. + static inline void + define(VALUE klass, T mid, type func) + { + F(klass, mid, func, N); + } + + /// @brief Defines klass#mid as func, whose arity is N. + /// @param[in] klass Where the method lives. + /// @param[in] mid Name of the method to define. + /// @param[in] func Function that implements klass#mid. + static inline void + define(VALUE klass, T mid, U func) + { + F(klass, mid, reinterpret_cast<type *>(func), N); + } + }; + + /// @cond INTERNAL_MACRO + template<int N, bool = false> struct specific : public engine<N, type *> {}; + template<bool b> struct specific<15, b> : public engine<15, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<14, b> : public engine<14, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<13, b> : public engine<13, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<12, b> : public engine<12, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<11, b> : public engine<11, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<10, b> : public engine<10, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 9, b> : public engine< 9, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 8, b> : public engine< 8, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 7, b> : public engine< 7, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 6, b> : public engine< 6, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 5, b> : public engine< 5, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 4, b> : public engine< 4, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 3, b> : public engine< 3, VALUE(*)(VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 2, b> : public engine< 2, VALUE(*)(VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 1, b> : public engine< 1, VALUE(*)(VALUE, VALUE)> {}; + template<bool b> struct specific< 0, b> : public engine< 0, VALUE(*)(VALUE)> {}; + template<bool b> struct specific<-1, b> : public engine<-1, VALUE(*)(int argc, VALUE argv, VALUE self)> { + using engine<-1, VALUE(*)(int argc, VALUE argv, VALUE self)>::define; + static inline void define(VALUE c, T m, VALUE(*f)(int argc, VALUE *argv, VALUE self)) { F(c, m, reinterpret_cast<type *>(f), -1); } + static inline void define(VALUE c, T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self)) { F(c, m, reinterpret_cast<type *>(f), -1); } + static inline void define(VALUE c, T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self, VALUE)) { F(c, m, reinterpret_cast<type *>(f), -1); } + }; + template<bool b> struct specific<-2, b> : public engine<-2, VALUE(*)(VALUE, VALUE)> {}; + /// @endcond +}; + +/* We could perhaps merge this struct into the one above using variadic + * template parameters if we could assume C++11, but sadly we cannot. */ +template<typename T, void (*F)(T mid, type func, int arity)> +struct driver0 { + template<int N, typename U> + struct engine { + RUBY_CXX_DEPRECATED("use of ANYARGS is deprecated") + static inline void + define(T mid, type func) + { + F(mid, func, N); + } + static inline void + define(T mid, U func) + { + F(mid, reinterpret_cast<type *>(func), N); + } + }; + /// @cond INTERNAL_MACRO + template<int N, bool = false> struct specific : public engine<N, type *> {}; + template<bool b> struct specific<15, b> : public engine<15, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<14, b> : public engine<14, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<13, b> : public engine<13, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<12, b> : public engine<12, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<11, b> : public engine<11, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific<10, b> : public engine<10, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 9, b> : public engine< 9, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 8, b> : public engine< 8, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 7, b> : public engine< 7, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 6, b> : public engine< 6, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 5, b> : public engine< 5, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 4, b> : public engine< 4, VALUE(*)(VALUE, VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 3, b> : public engine< 3, VALUE(*)(VALUE, VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 2, b> : public engine< 2, VALUE(*)(VALUE, VALUE, VALUE)> {}; + template<bool b> struct specific< 1, b> : public engine< 1, VALUE(*)(VALUE, VALUE)> {}; + template<bool b> struct specific< 0, b> : public engine< 0, VALUE(*)(VALUE)> {}; + template<bool b> struct specific<-1, b> : public engine<-1, VALUE(*)(int argc, VALUE argv, VALUE self)> { + using engine<-1, VALUE(*)(int argc, VALUE argv, VALUE self)>::define; + static inline void define(T m, VALUE(*f)(int argc, VALUE *argv, VALUE self)) { F(m, reinterpret_cast<type *>(f), -1); } + static inline void define(T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self)) { F(m, reinterpret_cast<type *>(f), -1); } + static inline void define(T m, VALUE(*f)(int argc, const VALUE *argv, VALUE self, VALUE)) { F(m, reinterpret_cast<type *>(f), -1); } + }; + template<bool b> struct specific<-2, b> : public engine<-2, VALUE(*)(VALUE, VALUE)> {}; + /// @endcond +}; + +/// @brief Dispatches appropriate driver for ::rb_define_method +struct rb_define_method : public driver <const char *, ::rb_define_method> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_method_id +struct rb_define_method_id : public driver <ID, ::rb_define_method_id> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_private_method +struct rb_define_private_method : public driver <const char *, ::rb_define_private_method> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_protected_method +struct rb_define_protected_method : public driver <const char *, ::rb_define_protected_method> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_singleton_method +struct rb_define_singleton_method : public driver <const char *, ::rb_define_singleton_method> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_module_function +struct rb_define_module_function : public driver <const char *, ::rb_define_module_function> {}; + +/// @brief Dispatches appropriate driver for ::rb_define_global_function +struct rb_define_global_function : public driver0<const char *, ::rb_define_global_function> {}; + +/// @brief Defines klass\#mid. +/// @param klass Where the method lives. +/// @copydetails #rb_define_global_function +#define rb_define_method(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_method::specific<arity>::define(klass, mid, func) + +/// @copydoc #rb_define_method +#define rb_define_method_id(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_method_id::specific<arity>::define(klass, mid, func) + +/// @brief Defines klass\#mid and makes it private. +/// @copydetails #rb_define_method +#define rb_define_private_method(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_private_method::specific<arity>::define(klass, mid, func) + +/// @brief Defines klass\#mid and makes it protected. +/// @copydetails #rb_define_method +#define rb_define_protected_method(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_protected_method::specific<arity>::define(klass, mid, func) + +/// @brief Defines klass.mid. +/// @copydetails #rb_define_method +#define rb_define_singleton_method(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_singleton_method::specific<arity>::define(klass, mid, func) + +/// @brief Defines klass\#mid and makes it a module function. +/// @copydetails #rb_define_method +#define rb_define_module_function(klass, mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_module_function::specific<arity>::define(klass, mid, func) + +/// @brief Defines ::rb_cKerbel \#mid. +/// @param mid Name of the defining method. +/// @param func Implementation of \#mid. +/// @param arity Arity of \#mid. +#define rb_define_global_function(mid, func, arity) ruby::backward::cxxanyargs::define_method::rb_define_global_function::specific<arity>::define(mid, func) + +}}}}} using namespace ruby::backward::cxxanyargs; #endif // RUBY_BACKWARD_CXXANYARGS_HPP diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index e96ac40747..9b3a2bcf81 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -2654,12 +2654,8 @@ void ruby_sig_finalize(void); RUBY_SYMBOL_EXPORT_END #if defined(__cplusplus) -#if 0 -{ /* satisfy cc-mode */ -#endif -} /* extern "C" { */ -extern "C++" { -#endif +#include "backward/cxxanyargs.hpp" +#else #if defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P) # define rb_f_notimplement_p(f) __builtin_types_compatible_p(__typeof__(f),__typeof__(rb_f_notimplement)) @@ -2937,6 +2933,8 @@ RB_METHOD_DEFINITION_DECL(rb_define_global_function, (1,2), (const char *name), #endif +#endif /* __cplusplus */ + #if defined(RUBY_DEVEL) && RUBY_DEVEL && (!defined(__cplusplus) || defined(RB_METHOD_DEFINITION_DECL)) # define RUBY_METHOD_FUNC(func) (func) #else @@ -2944,8 +2942,6 @@ RB_METHOD_DEFINITION_DECL(rb_define_global_function, (1,2), (const char *name), #endif #ifdef __cplusplus -#include "backward/cxxanyargs.hpp" - #if 0 { /* satisfy cc-mode */ #endif |