summaryrefslogtreecommitdiff
path: root/include/ruby/internal/iterator.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/internal/iterator.h')
-rw-r--r--include/ruby/internal/iterator.h481
1 files changed, 464 insertions, 17 deletions
diff --git a/include/ruby/internal/iterator.h b/include/ruby/internal/iterator.h
index a2aee15d31..5f706460f8 100644
--- a/include/ruby/internal/iterator.h
+++ b/include/ruby/internal/iterator.h
@@ -17,49 +17,496 @@
* recursively included from extension libraries written in C++.
* Do not expect for instance `__VA_ARGS__` is always available.
* We assume C99 for ruby itself but we don't assume languages of
- * extension libraries. They could be written in C++98.
+ * extension libraries. They could be written in C++98.
* @brief Block related APIs.
*/
+#include "ruby/internal/attr/deprecated.h"
#include "ruby/internal/attr/noreturn.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/value.h"
RBIMPL_SYMBOL_EXPORT_BEGIN()
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
#define RB_BLOCK_CALL_FUNC_STRICT 1
+
+/**
+ * @private
+ *
+ * @deprecated This macro once was a thing in the old days, but makes no sense
+ * any longer today. Exists here for backwards compatibility
+ * only. You can safely forget about it.
+ */
#define RUBY_BLOCK_CALL_FUNC_TAKES_BLOCKARG 1
+
+/**
+ * Shim for block function parameters. Historically ::rb_block_call_func_t had
+ * only two parameters. Over time it evolved to have much more than that. By
+ * using this macro you can absorb such API differences.
+ *
+ * ```CXX
+ * // This works since 2.1.0
+ * VALUE my_own_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(y, c));
+ * ```
+ */
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg) \
VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg
+
+/**
+ * This is the type of a function that the interpreter expect for C-backended
+ * blocks. Blocks are often written in Ruby. But C extensions might want to
+ * have their own blocks. In order to do so authors have to create a separate
+ * C function of this type, and pass its pointer to rb_block_call().
+ *
+ * ```CXX
+ * VALUE
+ * my_own_iterator(RB_BLOCK_CALL_FUNC_ARGLIST(y, c))
+ * {
+ * const auto plus = rb_intern("+");
+ * return rb_funcall(c, plus, 1, y);
+ * }
+ *
+ * VALUE
+ * my_own_method(VALUE self)
+ * {
+ * const auto each = rb_intern("each");
+ * return rb_block_call(self, each, 0, 0, my_own_iterator, self);
+ * }
+ * ```
+ */
typedef VALUE rb_block_call_func(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg));
+
+/**
+ * Shorthand type that represents an iterator-written-in-C function pointer.
+ */
typedef rb_block_call_func *rb_block_call_func_t;
-VALUE rb_each(VALUE);
-VALUE rb_yield(VALUE);
+/**
+ * This is a shorthand of calling `obj.each`.
+ *
+ * @param[in] obj The receiver.
+ * @return What `obj.each` returns.
+ *
+ * @internal
+ *
+ * Does anyone still need it? This API was to use with rb_iterate(), which is
+ * marked deprecated (see below). Old idiom to call an iterator was:
+ *
+ * ```CXX
+ * VALUE recv;
+ * VALUE iter_func(ANYARGS);
+ * VALUE iter_data;
+ * rb_iterate(rb_each, recv, iter_func, iter_data);
+ * ```
+ */
+VALUE rb_each(VALUE obj);
+
+/**
+ * Yields the block. In Ruby there is a concept called a block. You can pass
+ * one to a method. In a method, when called with a block, you can yield it
+ * using this function.
+ *
+ * ```CXX
+ * VALUE
+ * iterate(VALUE self)
+ * {
+ * extern int get_n(VALUE);
+ * extern VALUE get_v(VALUE, VALUE);
+ * const auto n = get_n(self);
+ *
+ * for (int i=0; i<n; i++) {
+ * auto v = get_v(self, i);
+ *
+ * rb_yield(v);
+ * }
+ * return self;
+ * }
+ * ```
+ *
+ * @param[in] val Passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield(VALUE val);
+
+/**
+ * Identical to rb_yield(), except it takes variadic number of parameters and
+ * pass them to the block.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] ... List of arguments passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
VALUE rb_yield_values(int n, ...);
+
+/**
+ * Identical to rb_yield_values(), except it takes the parameters as a C array
+ * instead of variadic arguments.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] argv List of arguments passed to the block.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
VALUE rb_yield_values2(int n, const VALUE *argv);
+
+/**
+ * Identical to rb_yield_values2(), except you can specify how to handle the
+ * last element of the given array.
+ *
+ * @param[in] n Number of parameters.
+ * @param[in] argv List of arguments passed to the block.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `ary`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `ary`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS makes no sense here.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
VALUE rb_yield_values_kw(int n, const VALUE *argv, int kw_splat);
-VALUE rb_yield_splat(VALUE);
-VALUE rb_yield_splat_kw(VALUE, int);
+
+/**
+ * Identical to rb_yield_values(), except it splats an array to generate the
+ * list of parameters.
+ *
+ * @param[in] ary Array to splat.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_splat(VALUE ary);
+
+/**
+ * Identical to rb_yield_splat(), except you can specify how to handle the last
+ * element of the given array.
+ *
+ * @param[in] ary Array to splat.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `ary`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `ary`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS makes no sense here.
+ * @exception rb_eLocalJumpError There is no block given.
+ * @return Evaluated value of the given block.
+ */
+VALUE rb_yield_splat_kw(VALUE ary, int kw_splat);
+
+/**
+ * Pass a passed block.
+ *
+ * Sometimes you want to "pass" a block form one method to another. Suppose
+ * you have this Ruby method `foo`:
+ *
+ * ```ruby
+ * def foo(x, y)
+ * x.open(y) do |*z|
+ * yield(*z)
+ * end
+ * end
+ * ```
+ *
+ * And suppose you want to translate this into C. Then rb_yield_block()
+ * function is usable in this situation.
+ *
+ * ```CXX
+ * VALUE
+ * foo_translated_into_C(VALUE self, VALUE x, VALUE y)
+ * {
+ * const auto open = rb_intern("open");
+ *
+ * return rb_block_call(x, open, 1, &y, rb_yield_block, Qfalse);
+ * // ^^^^^^^^^^^^^^ Here.
+ * }
+ * ```
+ *
+ * @see rb_funcall_passing_block
+ *
+ * @internal
+ *
+ * @shyouhei honestly doesn't understand why this is needed, given there
+ * already was rb_funcall_passing_block() at the time it was implemented. If
+ * somebody knows its raison d'etre, please improve the document :FIXME:
+ */
VALUE rb_yield_block(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); /* rb_block_call_func */
+
+/**
+ * Determines if the current method is given a keyword argument.
+ *
+ * @retval false No keyword argument is given.
+ * @retval true Keyword argument(s) are given.
+ * @ingroup defmethod
+ */
int rb_keyword_given_p(void);
+
+/**
+ * Determines if the current method is given a block.
+ *
+ * @retval false No block is given.
+ * @retval true A block is given.
+ * @ingroup defmethod
+ *
+ * @internal
+ *
+ * This function should have returned a bool. But at the time it was designed
+ * the project was entirely written in K&R C.
+ */
int rb_block_given_p(void);
+
+/**
+ * Declares that the current method needs a block.
+ *
+ * @exception rb_eLocalJumpError No block given.
+ * @ingroup defmethod
+ */
void rb_need_block(void);
-VALUE rb_iterate(VALUE(*)(VALUE),VALUE,rb_block_call_func_t,VALUE);
-DEPRECATED_BY(rb_block_call since 1.9, VALUE rb_iterate(VALUE(*)(VALUE),VALUE,rb_block_call_func_t,VALUE));
-VALUE rb_block_call(VALUE,ID,int,const VALUE*,rb_block_call_func_t,VALUE);
-VALUE rb_block_call_kw(VALUE,ID,int,const VALUE*,rb_block_call_func_t,VALUE,int);
-VALUE rb_rescue(VALUE(*)(VALUE),VALUE,VALUE(*)(VALUE,VALUE),VALUE);
-VALUE rb_rescue2(VALUE(*)(VALUE),VALUE,VALUE(*)(VALUE,VALUE),VALUE,...);
-VALUE rb_vrescue2(VALUE(*)(VALUE),VALUE,VALUE(*)(VALUE,VALUE),VALUE,va_list);
-VALUE rb_ensure(VALUE(*)(VALUE),VALUE,VALUE(*)(VALUE),VALUE);
-VALUE rb_catch(const char*,rb_block_call_func_t,VALUE);
-VALUE rb_catch_obj(VALUE,rb_block_call_func_t,VALUE);
+
+#ifndef __cplusplus
+RBIMPL_ATTR_DEPRECATED(("by: rb_block_call since 1.9"))
+#endif
+/**
+ * Old way to iterate a block.
+ *
+ * @deprecated This is an old API. Use rb_block_call() instead.
+ * @warning The passed function must at least once call a ruby method
+ * (to handle interrupts etc.)
+ * @param[in] func1 A function that could yield a value.
+ * @param[in,out] data1 Passed to `func1`
+ * @param[in] proc A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @return What `func1` returns.
+ */
+VALUE rb_iterate(VALUE (*func1)(VALUE), VALUE data1, rb_block_call_func_t proc, VALUE data2);
+
+#ifdef __cplusplus
+namespace ruby {
+namespace backward {
+/**
+ * Old way to iterate a block.
+ *
+ * @deprecated This is an old API. Use rb_block_call() instead.
+ * @warning The passed function must at least once call a ruby method
+ * (to handle interrupts etc.)
+ * @param[in] iter A function that could yield a value.
+ * @param[in,out] data1 Passed to `func1`
+ * @param[in] bl A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @return What `func1` returns.
+ */
+static inline VALUE
+rb_iterate_deprecated(VALUE (*iter)(VALUE), VALUE data1, rb_block_call_func_t bl, VALUE data2)
+{
+ return ::rb_iterate(iter, data1, bl, data2);
+}}}
+
+RBIMPL_ATTR_DEPRECATED(("by: rb_block_call since 1.9"))
+VALUE rb_iterate(VALUE (*func1)(VALUE), VALUE data1, rb_block_call_func_t proc, VALUE data2);
+#endif
+
+/**
+ * Identical to rb_funcallv(), except it additionally passes a function as a
+ * block. When the method yields, `proc` is called with the yielded value as
+ * its first argument, and `data2` as the second. Yielded values would be
+ * packed into an array if multiple values are yielded at once.
+ *
+ * @param[in,out] obj Receiver.
+ * @param[in] mid Method signature.
+ * @param[in] argc Number of arguments.
+ * @param[in] argv Arguments passed to `obj.mid`.
+ * @param[in] proc A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @return What `obj.mid` returns.
+ */
+VALUE rb_block_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t proc, VALUE data2);
+
+/**
+ * Identical to rb_funcallv_kw(), except it additionally passes a function as a
+ * block. It can also be seen as a routine identical to rb_block_call(),
+ * except it handles keyword-ness of `argv[argc-1]`.
+ *
+ * @param[in,out] obj Receiver.
+ * @param[in] mid Method signature.
+ * @param[in] argc Number of arguments including the keywords.
+ * @param[in] argv Arguments passed to `obj.mid`.
+ * @param[in] proc A function acts as a block.
+ * @param[in,out] data2 Passed to `proc` as the data2 parameter.
+ * @param[in] kw_splat Handling of keyword parameters:
+ * - RB_NO_KEYWORDS `argv`'s last is not a keyword argument.
+ * - RB_PASS_KEYWORDS `argv`'s last is a keyword argument.
+ * - RB_PASS_CALLED_KEYWORDS it depends if there is a passed block.
+ * @return What `obj.mid` returns.
+ */
+VALUE rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t proc, VALUE data2, int kw_splat);
+
+/**
+ * Identical to rb_rescue2(), except it does not take a list of exception
+ * classes. This is a shorthand of:
+ *
+ * ```CXX
+ * rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
+ * ```
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_rescue(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2);
+
+/**
+ * An equivalent of `rescue` clause.
+ *
+ * First it calls the function `b_proc` with `data1` as the argument. If
+ * nothing is thrown the function happily returns the return value of `b_proc`.
+ * When `b_proc` raises an exception, and the exception is a kind of one of the
+ * given exception classes, it then calls `r_proc` with `data2` and that
+ * exception. If the exception does not match any of them, it propagates.
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @param[in] ... 1 or more exception classes. Must be terminated by
+ * `(VALUE)0`
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_rescue2(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2, ...);
+
+/**
+ * Identical to rb_rescue2(), except it takes `va_list` instead of variadic
+ * number of arguments. This is exposed to 3rd parties because inline
+ * functions use it. Basically you don't have to bother.
+ *
+ * @param[in] b_proc A function which potentially raises an exception.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] r_proc A function which rescues an exception in `b_proc`.
+ * @param[in,out] data2 The first argument of `r_proc`.
+ * @param[in] ap 1 or more exception classes. Must be terminated by
+ * `(VALUE)0`
+ * @return The return value of `b_proc` if no exception occurs, or the
+ * return value of `r_proc` otherwise.
+ * @see rb_rescue
+ * @see rb_ensure
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_vrescue2(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*r_proc)(VALUE, VALUE), VALUE data2, va_list ap);
+
+/**
+ * An equivalent to `ensure` clause. Calls the function `b_proc` with `data1`
+ * as the argument, then calls `e_proc` with `data2` when execution terminated.
+ *
+ * @param[in] b_proc A function representing begin clause.
+ * @param[in,out] data1 Passed to `b_proc`.
+ * @param[in] e_proc A function representing ensure clause.
+ * @param[in,out] data2 Passed to `e_proc`.
+ * @retval RUBY_Qnil exception occurred inside of `b_proc`.
+ * @retval otherwise The return value of `b_proc`.
+ * @see rb_rescue
+ * @see rb_rescue2
+ * @see rb_protect
+ * @ingroup exception
+ */
+VALUE rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2);
+
+/**
+ * Executes the passed block and catches values thrown from inside of it.
+ *
+ * In case the block does not contain any throw`, this function returns the
+ * value of the last expression evaluated.
+ *
+ * ```CXX
+ * VALUE
+ * iter(RB_BLOCK_CALL_FUNC_ARGLIST(yielded, callback))
+ * {
+ * return INT2FIX(123);
+ * }
+ *
+ * VALUE
+ * method(VALUE self)
+ * {
+ * return rb_catch("tag", iter, Qnil); // returns 123
+ * }
+ * ```
+ *
+ * In case there do exist `throw`, Ruby searches up its execution context for a
+ * `catch` block. When a matching catch is found, the block stops executing
+ * and returns that thrown value instead.
+ *
+ * ```CXX
+ * VALUE
+ * iter(RB_BLOCK_CALL_FUNC_ARGLIST(yielded, callback))
+ * {
+ * rb_throw("tag", 456);
+ * return INT2FIX(123);
+ * }
+ *
+ * VALUE
+ * method(VALUE self)
+ * {
+ * return rb_catch("tag", iter, Qnil); // returns 456
+ * }
+ * ```
+ *
+ * @param[in] tag Arbitrary tag string.
+ * @param[in] func Function pointer that acts as a block.
+ * @param[in,out] data Extra parameter passed to `func`.
+ * @return Either caught value for `tag`, or the return value of `func`
+ * if nothing is thrown.
+ */
+VALUE rb_catch(const char *tag, rb_block_call_func_t func, VALUE data);
+
+/**
+ * Identical to rb_catch(), except it catches arbitrary Ruby objects.
+ *
+ * @param[in] tag Arbitrary tag object.
+ * @param[in] func Function pointer that acts as a block.
+ * @param[in,out] data Extra parameter passed to `func`.
+ * @return Either caught value for `tag`, or the return value of `func`
+ * if nothing is thrown.
+ */
+VALUE rb_catch_obj(VALUE tag, rb_block_call_func_t func, VALUE data);
RBIMPL_ATTR_NORETURN()
-void rb_throw(const char*,VALUE);
+/**
+ * Transfers control to the end of the active `catch` block waiting for `tag`.
+ * Raises rb_eUncughtThrow if there is no `catch` block for the tag. The
+ * second parameter supplies a return value for the `catch` block, which
+ * otherwise defaults to ::RUBY_Qnil. For examples, see rb_catch().
+ *
+ * @param[in] tag Tag string.
+ * @param[in] val Value to throw.
+ * @exception rb_eUncughtThrow There is no corresponding `catch` clause.
+ * @note It never returns.
+ */
+void rb_throw(const char *tag, VALUE val);
RBIMPL_ATTR_NORETURN()
-void rb_throw_obj(VALUE,VALUE);
+/**
+ * Identical to rb_throw(), except it allows arbitrary Ruby object to become a
+ * tag.
+ *
+ * @param[in] tag Arbitrary object.
+ * @param[in] val Value to throw.
+ * @exception rb_eUncughtThrow There is no corresponding `catch` clause.
+ * @note It never returns.
+ */
+void rb_throw_obj(VALUE tag, VALUE val);
RBIMPL_SYMBOL_EXPORT_END()