rbxx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +72 -0
- data/exe/rbxx +6 -0
- data/ext-cmake/FindRuby.cmake +42 -0
- data/ext-cmake/rbxx-config.cmake +25 -0
- data/include/rbxx/arg.hpp +347 -0
- data/include/rbxx/callback.hpp +54 -0
- data/include/rbxx/class.hpp +630 -0
- data/include/rbxx/data_object.hpp +221 -0
- data/include/rbxx/detail/ruby_include.hpp +36 -0
- data/include/rbxx/exception.hpp +116 -0
- data/include/rbxx/extension.hpp +23 -0
- data/include/rbxx/function.hpp +463 -0
- data/include/rbxx/module.hpp +69 -0
- data/include/rbxx/nogvl.hpp +146 -0
- data/include/rbxx/object.hpp +68 -0
- data/include/rbxx/operators.hpp +31 -0
- data/include/rbxx/policies.hpp +36 -0
- data/include/rbxx/protect.hpp +72 -0
- data/include/rbxx/rbxx.hpp +20 -0
- data/include/rbxx/registry.hpp +145 -0
- data/include/rbxx/stl/array.hpp +40 -0
- data/include/rbxx/stl/chrono.hpp +26 -0
- data/include/rbxx/stl/detail.hpp +60 -0
- data/include/rbxx/stl/filesystem.hpp +21 -0
- data/include/rbxx/stl/map.hpp +49 -0
- data/include/rbxx/stl/optional.hpp +23 -0
- data/include/rbxx/stl/set.hpp +30 -0
- data/include/rbxx/stl/tuple.hpp +74 -0
- data/include/rbxx/stl/variant.hpp +35 -0
- data/include/rbxx/stl/vector.hpp +34 -0
- data/include/rbxx/stl.hpp +11 -0
- data/include/rbxx/type_caster.hpp +247 -0
- data/include/rbxx/value.hpp +63 -0
- data/include/rbxx/version.hpp +14 -0
- data/lib/rbxx/cli.rb +127 -0
- data/lib/rbxx/mkmf.rb +144 -0
- data/lib/rbxx/rake_tasks.rb +44 -0
- data/lib/rbxx/version.rb +5 -0
- data/lib/rbxx.rb +8 -0
- data/single_include/rbxx/rbxx.hpp +3211 -0
- data/templates/CMakeLists.txt.erb +14 -0
- data/templates/Gemfile.erb +10 -0
- data/templates/README.md.erb +11 -0
- data/templates/Rakefile.erb +23 -0
- data/templates/ci.yml.erb +21 -0
- data/templates/extconf.rb.erb +5 -0
- data/templates/extconf_cmake.rb.erb +5 -0
- data/templates/extension.cpp.erb +23 -0
- data/templates/gemspec.erb +16 -0
- data/templates/lib.rb.erb +4 -0
- data/templates/release.yml.erb +21 -0
- data/templates/test.rb.erb +12 -0
- data/templates/version.rb.erb +5 -0
- metadata +99 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <rbxx/policies.hpp>
|
|
4
|
+
#include <rbxx/registry.hpp>
|
|
5
|
+
|
|
6
|
+
#include <memory>
|
|
7
|
+
#include <string>
|
|
8
|
+
#include <type_traits>
|
|
9
|
+
#include <utility>
|
|
10
|
+
|
|
11
|
+
namespace rbxx {
|
|
12
|
+
|
|
13
|
+
/// @brief A Ruby VALUE member that remains marked and compaction-safe inside a C++ object.
|
|
14
|
+
/// @code rbxx::member_value child{ruby_child}; @endcode
|
|
15
|
+
class member_value {
|
|
16
|
+
public:
|
|
17
|
+
member_value() noexcept { rb_gc_register_address(&stored_); }
|
|
18
|
+
explicit member_value(value initial) noexcept : stored_(initial.raw()) {
|
|
19
|
+
rb_gc_register_address(&stored_);
|
|
20
|
+
}
|
|
21
|
+
explicit member_value(VALUE initial) noexcept : stored_(initial) {
|
|
22
|
+
rb_gc_register_address(&stored_);
|
|
23
|
+
}
|
|
24
|
+
member_value(const member_value& other) noexcept : stored_(other.stored_) {
|
|
25
|
+
rb_gc_register_address(&stored_);
|
|
26
|
+
}
|
|
27
|
+
member_value(member_value&& other) noexcept : stored_(other.stored_) {
|
|
28
|
+
rb_gc_register_address(&stored_);
|
|
29
|
+
other.stored_ = Qnil;
|
|
30
|
+
}
|
|
31
|
+
member_value& operator=(const member_value& other) noexcept {
|
|
32
|
+
stored_ = other.stored_;
|
|
33
|
+
return *this;
|
|
34
|
+
}
|
|
35
|
+
member_value& operator=(member_value&& other) noexcept {
|
|
36
|
+
stored_ = other.stored_;
|
|
37
|
+
other.stored_ = Qnil;
|
|
38
|
+
return *this;
|
|
39
|
+
}
|
|
40
|
+
~member_value() noexcept { rb_gc_unregister_address(&stored_); }
|
|
41
|
+
|
|
42
|
+
[[nodiscard]] value get() const noexcept { return value{stored_}; }
|
|
43
|
+
void set(value updated) noexcept { stored_ = updated.raw(); }
|
|
44
|
+
|
|
45
|
+
private:
|
|
46
|
+
VALUE stored_ = Qnil;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
namespace detail {
|
|
50
|
+
|
|
51
|
+
template <typename T> struct is_non_bindable_class : std::false_type {};
|
|
52
|
+
template <typename T> struct is_non_bindable_class<std::unique_ptr<T>> : std::true_type {};
|
|
53
|
+
template <typename T> struct is_non_bindable_class<std::shared_ptr<T>> : std::true_type {};
|
|
54
|
+
|
|
55
|
+
enum class ownership { owned, borrowed, shared };
|
|
56
|
+
|
|
57
|
+
template <typename T> struct data_wrapper {
|
|
58
|
+
T* pointer = nullptr;
|
|
59
|
+
ownership mode = ownership::borrowed;
|
|
60
|
+
std::shared_ptr<T> shared_owner;
|
|
61
|
+
|
|
62
|
+
~data_wrapper() noexcept {
|
|
63
|
+
if (mode == ownership::owned) {
|
|
64
|
+
delete pointer;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
template <typename T> void data_mark(void*) noexcept {}
|
|
70
|
+
template <typename T> void data_compact(void*) noexcept {}
|
|
71
|
+
|
|
72
|
+
template <typename T> void data_free(void* raw) noexcept {
|
|
73
|
+
delete static_cast<data_wrapper<T>*>(raw);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
template <typename T> std::size_t data_size(const void* raw) noexcept {
|
|
77
|
+
const auto* wrapper = static_cast<const data_wrapper<T>*>(raw);
|
|
78
|
+
return sizeof(data_wrapper<T>) +
|
|
79
|
+
(wrapper != nullptr && wrapper->pointer != nullptr ? sizeof(T) : 0U);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
template <typename T> rb_data_type_t& typed_data_type() {
|
|
83
|
+
static auto* name = new std::string("rbxx::" + std::string(type_name<T>()));
|
|
84
|
+
static rb_data_type_t type = {
|
|
85
|
+
name->c_str(),
|
|
86
|
+
{data_mark<T>, data_free<T>, data_size<T>, data_compact<T>, {nullptr}},
|
|
87
|
+
nullptr,
|
|
88
|
+
nullptr,
|
|
89
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
90
|
+
};
|
|
91
|
+
return type;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
template <typename T> value wrap_data(std::unique_ptr<data_wrapper<T>> wrapper) {
|
|
95
|
+
class_info& information = registered_class<T>();
|
|
96
|
+
VALUE result = protect(rb_data_typed_object_wrap, information.ruby_class,
|
|
97
|
+
static_cast<void*>(wrapper.get()), information.data_type);
|
|
98
|
+
wrapper.release();
|
|
99
|
+
return value{result};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
template <typename T> value wrap_copy(const T& input) {
|
|
103
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
104
|
+
wrapper->pointer = new T(input);
|
|
105
|
+
wrapper->mode = ownership::owned;
|
|
106
|
+
return wrap_data(std::move(wrapper));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
template <typename T> value wrap_move(T&& input) {
|
|
110
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
111
|
+
wrapper->pointer = new T(std::move(input));
|
|
112
|
+
wrapper->mode = ownership::owned;
|
|
113
|
+
return wrap_data(std::move(wrapper));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
template <typename T> value wrap_reference(T* input) {
|
|
117
|
+
if (input == nullptr) {
|
|
118
|
+
return value{Qnil};
|
|
119
|
+
}
|
|
120
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
121
|
+
wrapper->pointer = input;
|
|
122
|
+
wrapper->mode = ownership::borrowed;
|
|
123
|
+
return wrap_data(std::move(wrapper));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
template <typename T> value wrap_take(std::unique_ptr<T> input) {
|
|
127
|
+
if (!input) {
|
|
128
|
+
return value{Qnil};
|
|
129
|
+
}
|
|
130
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
131
|
+
wrapper->pointer = input.release();
|
|
132
|
+
wrapper->mode = ownership::owned;
|
|
133
|
+
return wrap_data(std::move(wrapper));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
template <typename T> value wrap_shared(std::shared_ptr<T> input) {
|
|
137
|
+
if (!input) {
|
|
138
|
+
return value{Qnil};
|
|
139
|
+
}
|
|
140
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
141
|
+
wrapper->pointer = input.get();
|
|
142
|
+
wrapper->mode = ownership::shared;
|
|
143
|
+
wrapper->shared_owner = std::move(input);
|
|
144
|
+
return wrap_data(std::move(wrapper));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
template <typename T, typename Base = void>
|
|
148
|
+
void register_data_class(VALUE ruby_class,
|
|
149
|
+
std::source_location location = std::source_location::current()) {
|
|
150
|
+
rb_data_type_t& type = typed_data_type<T>();
|
|
151
|
+
if constexpr (!std::is_void_v<Base>) {
|
|
152
|
+
type.parent = &typed_data_type<Base>();
|
|
153
|
+
}
|
|
154
|
+
type_registry::instance().add<T, Base>(ruby_class, &type, location);
|
|
155
|
+
type_registry::instance().set_native_pointer<T>(
|
|
156
|
+
[](void* raw) { return static_cast<void*>(static_cast<data_wrapper<T>*>(raw)->pointer); });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
template <typename T> VALUE allocate_data_object(VALUE klass) {
|
|
160
|
+
VALUE result = Qnil;
|
|
161
|
+
VALUE pending = Qnil;
|
|
162
|
+
try {
|
|
163
|
+
auto wrapper = std::make_unique<data_wrapper<T>>();
|
|
164
|
+
result = protect(rb_data_typed_object_wrap, klass, static_cast<void*>(wrapper.get()),
|
|
165
|
+
&typed_data_type<T>());
|
|
166
|
+
wrapper.release();
|
|
167
|
+
} catch (...) {
|
|
168
|
+
pending = translate_current_exception();
|
|
169
|
+
}
|
|
170
|
+
if (!NIL_P(pending)) {
|
|
171
|
+
rb_exc_raise(pending);
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
template <typename T> data_wrapper<T>& exact_wrapper(value self) {
|
|
177
|
+
if (!RB_TYPE_P(self.raw(), T_DATA) || !RTYPEDDATA_P(self.raw()) ||
|
|
178
|
+
RTYPEDDATA_TYPE(self.raw()) != &typed_data_type<T>()) {
|
|
179
|
+
throw ruby_error(make_exception(rb_eTypeError, "rbxx: unexpected TypedData class"));
|
|
180
|
+
}
|
|
181
|
+
return *static_cast<data_wrapper<T>*>(RTYPEDDATA_DATA(self.raw()));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
} // namespace detail
|
|
185
|
+
|
|
186
|
+
template <typename T>
|
|
187
|
+
struct type_caster<T, std::enable_if_t<std::is_class_v<T> && !std::is_same_v<T, object> &&
|
|
188
|
+
!detail::is_non_bindable_class<T>::value>> {
|
|
189
|
+
static constexpr std::string_view name = "bound C++ object";
|
|
190
|
+
static T& load(value input) { return detail::load_registered<T>(input); }
|
|
191
|
+
static value dump(const T& input) { return detail::wrap_copy(input); }
|
|
192
|
+
static value dump(T&& input) { return detail::wrap_move(std::move(input)); }
|
|
193
|
+
static bool matches(value input) noexcept { return detail::registered_matches<T>(input); }
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
template <typename T> struct type_caster<T*> {
|
|
197
|
+
static constexpr std::string_view name = "bound C++ object or nil";
|
|
198
|
+
static T* load(value input) {
|
|
199
|
+
return input.is_nil() ? nullptr : std::addressof(detail::load_registered<T>(input));
|
|
200
|
+
}
|
|
201
|
+
static value dump(T* input) { return detail::wrap_reference(input); }
|
|
202
|
+
static bool matches(value input) noexcept {
|
|
203
|
+
return input.is_nil() || detail::registered_matches<T>(input);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
template <typename T> struct type_caster<std::unique_ptr<T>> {
|
|
208
|
+
static constexpr std::string_view name = "owned C++ object";
|
|
209
|
+
static value dump(std::unique_ptr<T> input) { return detail::wrap_take(std::move(input)); }
|
|
210
|
+
static bool matches(value) noexcept { return false; }
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
template <typename T> struct type_caster<std::shared_ptr<T>> {
|
|
214
|
+
static constexpr std::string_view name = "shared C++ object";
|
|
215
|
+
static value dump(std::shared_ptr<T> input) { return detail::wrap_shared(std::move(input)); }
|
|
216
|
+
static bool matches(value input) noexcept {
|
|
217
|
+
return RB_TYPE_P(input.raw(), T_DATA) && RTYPEDDATA_P(input.raw());
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
} // namespace rbxx
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#if defined(_MSC_VER)
|
|
4
|
+
#ifndef NOMINMAX
|
|
5
|
+
#define NOMINMAX
|
|
6
|
+
#endif
|
|
7
|
+
#pragma warning(push)
|
|
8
|
+
#pragma warning(disable : 4127 4244 4267 4996)
|
|
9
|
+
#else
|
|
10
|
+
#pragma GCC diagnostic push
|
|
11
|
+
#pragma GCC diagnostic ignored "-Wconversion"
|
|
12
|
+
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
13
|
+
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
|
14
|
+
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
#include <ruby.h>
|
|
18
|
+
#include <ruby/thread.h>
|
|
19
|
+
#include <ruby/version.h>
|
|
20
|
+
|
|
21
|
+
#if defined(_MSC_VER)
|
|
22
|
+
#pragma warning(pop)
|
|
23
|
+
#else
|
|
24
|
+
#pragma GCC diagnostic pop
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
#define RBXX_RUBY_VERSION_MAJOR RUBY_API_VERSION_MAJOR
|
|
28
|
+
#define RBXX_RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
|
|
29
|
+
#define RBXX_RUBY_VERSION_TEENY RUBY_API_VERSION_TEENY
|
|
30
|
+
|
|
31
|
+
#if RUBY_API_VERSION_MAJOR < 4
|
|
32
|
+
// CRuby exports this debug helper before 4.0 but does not declare it in ruby/thread.h.
|
|
33
|
+
extern "C" int ruby_thread_has_gvl_p(void);
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
static_assert(RUBY_API_VERSION_MAJOR >= 3, "rbxx requires CRuby 3.1 or newer");
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <rbxx/object.hpp>
|
|
4
|
+
|
|
5
|
+
#include <exception>
|
|
6
|
+
#include <new>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
#include <string>
|
|
9
|
+
|
|
10
|
+
namespace rbxx {
|
|
11
|
+
|
|
12
|
+
/// @brief A C++ exception that preserves the original Ruby exception object.
|
|
13
|
+
/// @code catch (const rbxx::ruby_error& error) { error.reraise(); } @endcode
|
|
14
|
+
class ruby_error : public std::exception {
|
|
15
|
+
public:
|
|
16
|
+
/// @brief Pins a Ruby exception for propagation through C++ frames.
|
|
17
|
+
explicit ruby_error(value exception) : exception_(exception) {}
|
|
18
|
+
explicit ruby_error(VALUE exception) : exception_(exception) {}
|
|
19
|
+
|
|
20
|
+
/// @brief Returns a stable description for C++ diagnostics.
|
|
21
|
+
[[nodiscard]] const char* what() const noexcept override { return "Ruby exception"; }
|
|
22
|
+
|
|
23
|
+
/// @brief Returns the preserved Ruby exception.
|
|
24
|
+
[[nodiscard]] value exception() const noexcept { return exception_.get(); }
|
|
25
|
+
|
|
26
|
+
/// @brief Returns the Ruby exception class name.
|
|
27
|
+
[[nodiscard]] std::string ruby_class_name() const;
|
|
28
|
+
|
|
29
|
+
/// @brief Returns the Ruby exception message.
|
|
30
|
+
[[nodiscard]] std::string message() const;
|
|
31
|
+
|
|
32
|
+
/// @brief Throws a copy so the outer Ruby boundary can transparently re-raise it.
|
|
33
|
+
[[noreturn]] void reraise() const { throw *this; }
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
object exception_;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
namespace detail {
|
|
40
|
+
|
|
41
|
+
inline VALUE exception_class_name(VALUE exception) { return rb_class_name(CLASS_OF(exception)); }
|
|
42
|
+
|
|
43
|
+
inline VALUE exception_message(VALUE exception) {
|
|
44
|
+
VALUE message = rb_funcall(exception, rb_intern("message"), 0);
|
|
45
|
+
return rb_obj_as_string(message);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
inline std::string protected_exception_string(VALUE exception, VALUE (*function)(VALUE)) {
|
|
49
|
+
int state = 0;
|
|
50
|
+
VALUE string = rb_protect(function, exception, &state);
|
|
51
|
+
if (state != 0) {
|
|
52
|
+
VALUE nested = rb_errinfo();
|
|
53
|
+
rb_set_errinfo(Qnil);
|
|
54
|
+
throw ruby_error(nested);
|
|
55
|
+
}
|
|
56
|
+
return std::string(RSTRING_PTR(string), static_cast<std::size_t>(RSTRING_LEN(string)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
struct exception_creation {
|
|
60
|
+
VALUE klass;
|
|
61
|
+
const char* message;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
inline VALUE create_exception(VALUE opaque) {
|
|
65
|
+
auto* creation = reinterpret_cast<exception_creation*>(opaque);
|
|
66
|
+
return rb_exc_new_cstr(creation->klass, creation->message);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
inline VALUE make_exception(VALUE klass, const char* message) noexcept {
|
|
70
|
+
exception_creation creation{klass, message};
|
|
71
|
+
int state = 0;
|
|
72
|
+
VALUE result = rb_protect(create_exception, reinterpret_cast<VALUE>(&creation), &state);
|
|
73
|
+
if (state == 0) {
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
result = rb_errinfo();
|
|
78
|
+
rb_set_errinfo(Qnil);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// @brief Converts the active C++ exception into a Ruby exception VALUE without raising it.
|
|
83
|
+
/// @code catch (...) { pending = rbxx::detail::translate_current_exception(); } @endcode
|
|
84
|
+
inline VALUE translate_current_exception() noexcept {
|
|
85
|
+
try {
|
|
86
|
+
throw;
|
|
87
|
+
} catch (const ruby_error& error) {
|
|
88
|
+
return error.exception().raw();
|
|
89
|
+
} catch (const std::invalid_argument& error) {
|
|
90
|
+
return make_exception(rb_eArgError, error.what());
|
|
91
|
+
} catch (const std::out_of_range& error) {
|
|
92
|
+
return make_exception(rb_eRangeError, error.what());
|
|
93
|
+
} catch (const std::range_error& error) {
|
|
94
|
+
return make_exception(rb_eRangeError, error.what());
|
|
95
|
+
} catch (const std::bad_alloc& error) {
|
|
96
|
+
return make_exception(rb_eNoMemError, error.what());
|
|
97
|
+
} catch (const std::domain_error& error) {
|
|
98
|
+
return make_exception(rb_eMathDomainError, error.what());
|
|
99
|
+
} catch (const std::exception& error) {
|
|
100
|
+
return make_exception(rb_eRuntimeError, error.what());
|
|
101
|
+
} catch (...) {
|
|
102
|
+
return make_exception(rb_eRuntimeError, "unknown C++ exception");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} // namespace detail
|
|
107
|
+
|
|
108
|
+
inline std::string ruby_error::ruby_class_name() const {
|
|
109
|
+
return detail::protected_exception_string(exception_.raw(), detail::exception_class_name);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
inline std::string ruby_error::message() const {
|
|
113
|
+
return detail::protected_exception_string(exception_.raw(), detail::exception_message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
} // namespace rbxx
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <rbxx/exception.hpp>
|
|
4
|
+
|
|
5
|
+
#if defined(_WIN32)
|
|
6
|
+
#define RBXX_EXPORT __declspec(dllexport)
|
|
7
|
+
#else
|
|
8
|
+
#define RBXX_EXPORT __attribute__((visibility("default")))
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
#define RBXX_EXTENSION(name) \
|
|
12
|
+
static void rbxx_init_body_##name(); \
|
|
13
|
+
extern "C" RBXX_EXPORT void Init_##name() { \
|
|
14
|
+
VALUE rbxx_pending_exception = Qnil; \
|
|
15
|
+
try { \
|
|
16
|
+
rbxx_init_body_##name(); \
|
|
17
|
+
return; \
|
|
18
|
+
} catch (...) { \
|
|
19
|
+
rbxx_pending_exception = ::rbxx::detail::translate_current_exception(); \
|
|
20
|
+
} \
|
|
21
|
+
rb_exc_raise(rbxx_pending_exception); \
|
|
22
|
+
} \
|
|
23
|
+
static void rbxx_init_body_##name()
|