The mruby-error mrbgem provides a set of C-level APIs for structured exception handling within mruby. These functions allow C extensions or embedded mruby code to implement error handling patterns similar to Ruby's begin, rescue, and ensure keywords. This is particularly useful when writing C code that needs to interact with mruby's exception system in a robust way.
The mrb_protect function is used to execute a C function (body) and capture any exceptions that might be raised during its execution. This allows you to run potentially unsafe operations and handle errors gracefully.
C Signature:
mrb_value mrb_protect(mrb_state *mrb, mrb_func_t body, mrb_value data, mrb_bool *state);Parameters:
mrb_state *mrb: The current mruby state.mrb_func_t body: A function pointer to the C function to be executed. This function should have the signaturemrb_value (*body)(mrb_state *mrb, mrb_value data).mrb_value data: Amrb_valuethat will be passed as an argument to thebodyfunction.mrb_bool *state: A pointer to a boolean. Aftermrb_protectreturns, this boolean will be:FALSE(0) if thebodyfunction executed without raising an exception.TRUE(1) if an exception was raised within thebodyfunction.
Return Value:
- If no exception occurs (
*stateisFALSE),mrb_protectreturns the value returned by thebodyfunction. - If an exception occurs (
*stateisTRUE),mrb_protectreturns the exception object.
The mrb_ensure function ensures that a specific C function (ensure) is executed, regardless of whether another C function (body) completes normally or raises an exception. This is analogous to Ruby's ensure clause.
C Signature:
mrb_value mrb_ensure(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t ensure, mrb_value e_data);Parameters:
mrb_state *mrb: The current mruby state.mrb_func_t body: A function pointer to the main C function to be executed.mrb_value b_data: Amrb_valuepassed as data to thebodyfunction.mrb_func_t ensure: A function pointer to the C function that will always be executed after thebodyfunction.mrb_value e_data: Amrb_valuepassed as data to theensurefunction.
Behavior:
The body function is executed first. After its completion (either normally or due to an exception), the ensure function is executed. If the body function raised an exception, that exception is re-thrown after the ensure function has finished. The return value of mrb_ensure is the result of the body function if no exception occurred.
The mrb_rescue function executes a C function (body) and, if an exception that is a StandardError (or a subclass of StandardError) is raised, it executes a specified rescue C function. This is similar to a rescue clause in Ruby that doesn't specify a particular exception type (which defaults to StandardError).
C Signature:
mrb_value mrb_rescue(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data);Parameters:
mrb_state *mrb: The current mruby state.mrb_func_t body: A function pointer to the main C function to be executed.mrb_value b_data: Amrb_valuepassed as data to thebodyfunction.mrb_func_t rescue: A function pointer to the C function that will be executed if aStandardError(or its subclass) is caught.mrb_value r_data: Amrb_valuepassed as data to therescuefunction.
Behavior:
If the body function executes without raising an exception, its result is returned. If a StandardError (or one of its descendants) is raised, the rescue function is executed, and its result becomes the return value of mrb_rescue. If an exception occurs that is not a StandardError (or its subclass), it is not caught by this function and will propagate up the call stack. Similarly, if the rescue block itself raises an exception, that exception will propagate.
The mrb_rescue_exceptions function provides more fine-grained exception handling than mrb_rescue. It executes a C function (body) and, if an exception matching one of the specified classes is raised, it executes a rescue C function. This is analogous to Ruby's rescue SpecificError1, SpecificError2 => e syntax.
C Signature:
mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_value b_data, mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes);Parameters:
mrb_state *mrb: The current mruby state.mrb_func_t body: A function pointer to the main C function to be executed.mrb_value b_data: Amrb_valuepassed as data to thebodyfunction.mrb_func_t rescue: A function pointer to the C function that will be executed if a matching exception is caught.mrb_value r_data: Amrb_valuepassed as data to therescuefunction.mrb_int len: The number of exception classes provided in theclassesarray.struct RClass **classes: An array of pointers to mrubyRClassobjects representing the exception classes to be rescued.
Behavior:
If the body function executes without raising an exception, its result is returned. If an exception is raised that is an instance of one of the classes specified in the classes array (or a subclass of one of them), the rescue function is executed, and its result becomes the return value of mrb_rescue_exceptions. If an exception occurs that does not match any of the specified classes, it is not caught and will propagate. If the rescue block itself raises an exception, that exception will propagate.
The C functions provided by this mrbgem are typically used when writing mruby C extensions that need to interact with Ruby code or manage resources carefully in the presence of potential exceptions.
For concrete examples of how these functions are used, please refer to the test files within this mrbgem:
test/exception.c: Shows how these C functions are called directly.test/exception.rb: Demonstrates the behavior of these C functions from the Ruby side, through theExceptionTestmodule (defined intest/exception.c).
These tests illustrate how to set up callback functions and how the error handling mechanisms behave in practice.