qml 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +46 -0
- data/.rspec +2 -0
- data/.travis.yml +15 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +351 -0
- data/Rakefile +6 -0
- data/examples/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/examples/fizzbuzz/fizzbuzz.rb +43 -0
- data/examples/fizzbuzz/main.qml +38 -0
- data/examples/imageprovider/imageprovider.rb +57 -0
- data/examples/imageprovider/main.qml +51 -0
- data/examples/todo/main.qml +70 -0
- data/examples/todo/todo.rb +36 -0
- data/examples/twitter/main.qml +36 -0
- data/examples/twitter/twitter.rb +55 -0
- data/ext/qml/accessclass.cpp +71 -0
- data/ext/qml/accessclass.h +19 -0
- data/ext/qml/accessobject.cpp +30 -0
- data/ext/qml/accessobject.h +22 -0
- data/ext/qml/application.cpp +54 -0
- data/ext/qml/application.h +17 -0
- data/ext/qml/common.h +18 -0
- data/ext/qml/ext_accesssupport.cpp +77 -0
- data/ext/qml/ext_accesssupport.h +42 -0
- data/ext/qml/ext_gcmarker.cpp +39 -0
- data/ext/qml/ext_gcmarker.h +27 -0
- data/ext/qml/ext_kernel.cpp +62 -0
- data/ext/qml/ext_kernel.h +11 -0
- data/ext/qml/ext_metaobject.cpp +410 -0
- data/ext/qml/ext_metaobject.h +62 -0
- data/ext/qml/ext_pluginloader.cpp +55 -0
- data/ext/qml/ext_pluginloader.h +32 -0
- data/ext/qml/ext_pointer.cpp +134 -0
- data/ext/qml/ext_pointer.h +43 -0
- data/ext/qml/ext_testutil.cpp +42 -0
- data/ext/qml/ext_testutil.h +11 -0
- data/ext/qml/extconf.rb +84 -0
- data/ext/qml/foreignclass.cpp +72 -0
- data/ext/qml/foreignclass.h +88 -0
- data/ext/qml/foreignmetaobject.cpp +345 -0
- data/ext/qml/foreignmetaobject.h +46 -0
- data/ext/qml/foreignobject.cpp +22 -0
- data/ext/qml/foreignobject.h +21 -0
- data/ext/qml/functioninfo.h +16 -0
- data/ext/qml/init.cpp +69 -0
- data/ext/qml/listmodel.cpp +112 -0
- data/ext/qml/listmodel.h +43 -0
- data/ext/qml/markable.h +12 -0
- data/ext/qml/objectdata.cpp +26 -0
- data/ext/qml/objectdata.h +20 -0
- data/ext/qml/objectgc.cpp +69 -0
- data/ext/qml/objectgc.h +28 -0
- data/ext/qml/plugins/core/applicationextension.cpp +34 -0
- data/ext/qml/plugins/core/applicationextension.h +28 -0
- data/ext/qml/plugins/core/componentextension.cpp +41 -0
- data/ext/qml/plugins/core/componentextension.h +28 -0
- data/ext/qml/plugins/core/contextextension.cpp +39 -0
- data/ext/qml/plugins/core/contextextension.h +29 -0
- data/ext/qml/plugins/core/core.pro +29 -0
- data/ext/qml/plugins/core/coreplugin.cpp +87 -0
- data/ext/qml/plugins/core/coreplugin.h +49 -0
- data/ext/qml/plugins/core/engineextension.cpp +27 -0
- data/ext/qml/plugins/core/engineextension.h +28 -0
- data/ext/qml/plugins/core/imageprovider.cpp +38 -0
- data/ext/qml/plugins/core/imageprovider.h +18 -0
- data/ext/qml/plugins/core/imagerequestpromise.cpp +19 -0
- data/ext/qml/plugins/core/imagerequestpromise.h +21 -0
- data/ext/qml/plugins/core/qmlexception.cpp +11 -0
- data/ext/qml/plugins/core/qmlexception.h +17 -0
- data/ext/qml/plugins/testutil/objectlifechecker.cpp +17 -0
- data/ext/qml/plugins/testutil/objectlifechecker.h +24 -0
- data/ext/qml/plugins/testutil/ownershiptest.cpp +26 -0
- data/ext/qml/plugins/testutil/ownershiptest.h +30 -0
- data/ext/qml/plugins/testutil/testobject.cpp +6 -0
- data/ext/qml/plugins/testutil/testobject.h +108 -0
- data/ext/qml/plugins/testutil/testobjectsubclass.cpp +10 -0
- data/ext/qml/plugins/testutil/testobjectsubclass.h +19 -0
- data/ext/qml/plugins/testutil/testutil.pro +20 -0
- data/ext/qml/plugins/testutil/testutilplugin.cpp +47 -0
- data/ext/qml/plugins/testutil/testutilplugin.h +32 -0
- data/ext/qml/qmltyperegisterer.cpp +74 -0
- data/ext/qml/qmltyperegisterer.h +30 -0
- data/ext/qml/rubyclass.cpp +94 -0
- data/ext/qml/rubyclass.h +234 -0
- data/ext/qml/rubyvalue.cpp +690 -0
- data/ext/qml/rubyvalue.h +256 -0
- data/ext/qml/signalforwarder.cpp +66 -0
- data/ext/qml/signalforwarder.h +29 -0
- data/ext/qml/util.cpp +120 -0
- data/ext/qml/util.h +101 -0
- data/ext/qml/valuereference.cpp +50 -0
- data/ext/qml/valuereference.h +22 -0
- data/ext/qml/weakvaluereference.cpp +27 -0
- data/ext/qml/weakvaluereference.h +19 -0
- data/lib/qml.rb +41 -0
- data/lib/qml/access.rb +137 -0
- data/lib/qml/application.rb +139 -0
- data/lib/qml/class_builder.rb +126 -0
- data/lib/qml/component.rb +53 -0
- data/lib/qml/context.rb +71 -0
- data/lib/qml/data.rb +2 -0
- data/lib/qml/data/array_model.rb +103 -0
- data/lib/qml/data/error.rb +5 -0
- data/lib/qml/data/list_model.rb +146 -0
- data/lib/qml/dispatchable.rb +34 -0
- data/lib/qml/dispatcher.rb +61 -0
- data/lib/qml/engine.rb +54 -0
- data/lib/qml/error_converter.rb +15 -0
- data/lib/qml/errors.rb +26 -0
- data/lib/qml/geometry.rb +3 -0
- data/lib/qml/geometry/point.rb +5 -0
- data/lib/qml/geometry/rectangle.rb +5 -0
- data/lib/qml/geometry/size.rb +5 -0
- data/lib/qml/image_provider.rb +87 -0
- data/lib/qml/meta_object.rb +20 -0
- data/lib/qml/models.rb +1 -0
- data/lib/qml/name_helper.rb +12 -0
- data/lib/qml/platform.rb +15 -0
- data/lib/qml/plugin_loader.rb +46 -0
- data/lib/qml/plugins.rb +26 -0
- data/lib/qml/qml.rb +1 -0
- data/lib/qml/qt.rb +6 -0
- data/lib/qml/qt_classes.rb +9 -0
- data/lib/qml/qt_object_base.rb +108 -0
- data/lib/qml/reactive.rb +8 -0
- data/lib/qml/reactive/bindable.rb +79 -0
- data/lib/qml/reactive/chained_signal.rb +25 -0
- data/lib/qml/reactive/error.rb +5 -0
- data/lib/qml/reactive/object.rb +278 -0
- data/lib/qml/reactive/property.rb +19 -0
- data/lib/qml/reactive/signal.rb +116 -0
- data/lib/qml/reactive/signal_spy.rb +27 -0
- data/lib/qml/reactive/signals/map_signal.rb +21 -0
- data/lib/qml/reactive/signals/merge_signal.rb +21 -0
- data/lib/qml/reactive/signals/select_signal.rb +21 -0
- data/lib/qml/reactive/simple_property.rb +17 -0
- data/lib/qml/reactive/unbound_property.rb +42 -0
- data/lib/qml/reactive/unbound_signal.rb +51 -0
- data/lib/qml/root_path.rb +3 -0
- data/lib/qml/test_util.rb +1 -0
- data/lib/qml/test_util/object_life_checker.rb +17 -0
- data/lib/qml/version.rb +3 -0
- data/lib/qml/wrappable.rb +9 -0
- data/qml.gemspec +28 -0
- data/spec/assets/testobj.qml +5 -0
- data/spec/qml/.access_spec.rb.swp +0 -0
- data/spec/qml/access_spec.rb +162 -0
- data/spec/qml/application_spec.rb +43 -0
- data/spec/qml/component_spec.rb +44 -0
- data/spec/qml/context_spec.rb +43 -0
- data/spec/qml/conversion_spec.rb +59 -0
- data/spec/qml/data/array_model_spec.rb +215 -0
- data/spec/qml/dispatchable_spec.rb +26 -0
- data/spec/qml/dispatcher_spec.rb +48 -0
- data/spec/qml/geometry/point_spec.rb +4 -0
- data/spec/qml/geometry/rectangle_spec.rb +4 -0
- data/spec/qml/geometry/size_spec.rb +4 -0
- data/spec/qml/plugin_loader_spec.rb +33 -0
- data/spec/qml/qt_object_base_spec.rb +119 -0
- data/spec/qml/reactive/object_spec.rb +273 -0
- data/spec/qml/reactive/property_spec.rb +70 -0
- data/spec/qml/reactive/signal_spec.rb +191 -0
- data/spec/qml/reactive/signal_spy_spec.rb +26 -0
- data/spec/qml/test_object_spec.rb +186 -0
- data/spec/qml_spec.rb +7 -0
- data/spec/spec_helper.rb +5 -0
- metadata +321 -0
data/ext/qml/rubyvalue.h
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "util.h"
|
3
|
+
#include <ruby.h>
|
4
|
+
#include <array>
|
5
|
+
|
6
|
+
class QVariant;
|
7
|
+
|
8
|
+
namespace RubyQml {
|
9
|
+
|
10
|
+
namespace detail {
|
11
|
+
template <typename T, typename Enable = void> struct Conversion
|
12
|
+
{
|
13
|
+
static T from(RubyValue x);
|
14
|
+
static RubyValue to(typename std::conditional<std::is_scalar<T>::value, T, const T &>::type str);
|
15
|
+
};
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
#define RUBYQML_INTERN(str) \
|
20
|
+
[] { static auto id = rb_intern(str); return id; }()
|
21
|
+
|
22
|
+
class RubyModule;
|
23
|
+
|
24
|
+
class RubyValue
|
25
|
+
{
|
26
|
+
public:
|
27
|
+
RubyValue() = default;
|
28
|
+
RubyValue(VALUE value) : mValue(value) {}
|
29
|
+
|
30
|
+
template <typename T> static RubyValue from(const T &value)
|
31
|
+
{
|
32
|
+
return detail::Conversion<T>::to(value);
|
33
|
+
}
|
34
|
+
|
35
|
+
template <typename T> T to() const
|
36
|
+
{
|
37
|
+
return detail::Conversion<T>::from(*this);
|
38
|
+
}
|
39
|
+
|
40
|
+
operator VALUE() const { return mValue; }
|
41
|
+
|
42
|
+
template <typename ... TArgs>
|
43
|
+
RubyValue send(ID method, TArgs ... args) const
|
44
|
+
{
|
45
|
+
constexpr int argc = sizeof...(args);
|
46
|
+
std::array<VALUE, argc> argv = {{ VALUE(args)... }};
|
47
|
+
return protect([&] {
|
48
|
+
return rb_funcall2(mValue, method, argc, argv.data());
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
52
|
+
template <typename ... TArgs>
|
53
|
+
RubyValue send(const char *method, TArgs ... args) const
|
54
|
+
{
|
55
|
+
return send(rb_intern(method), args...);
|
56
|
+
}
|
57
|
+
|
58
|
+
bool operator==(const RubyValue &other) const { return mValue == other.mValue; }
|
59
|
+
bool operator!=(const RubyValue &other) const { return !operator==(other); }
|
60
|
+
explicit operator bool() const { return RTEST(mValue); }
|
61
|
+
|
62
|
+
bool isKindOf(const RubyModule &module) const;
|
63
|
+
bool isConvertibleTo(int metaType) const;
|
64
|
+
int defaultMetaType() const;
|
65
|
+
|
66
|
+
static RubyValue fromVariant(const QVariant &value);
|
67
|
+
QVariant toVariant() const;
|
68
|
+
QVariant toVariant(int type) const;
|
69
|
+
static RubyValue fromQObject(QObject *obj, bool implicit = true);
|
70
|
+
QObject *toQObject() const;
|
71
|
+
|
72
|
+
ID toID() const;
|
73
|
+
static RubyValue fromID(ID id) { return ID2SYM(id); }
|
74
|
+
|
75
|
+
static void addEnumeratorMetaType(int metaType);
|
76
|
+
|
77
|
+
private:
|
78
|
+
volatile VALUE mValue = Qnil;
|
79
|
+
};
|
80
|
+
|
81
|
+
namespace detail {
|
82
|
+
|
83
|
+
template <>
|
84
|
+
struct Conversion<RubyValue>
|
85
|
+
{
|
86
|
+
static RubyValue from(RubyValue x) { return x; }
|
87
|
+
static RubyValue to(RubyValue x) { return x; }
|
88
|
+
};
|
89
|
+
|
90
|
+
template <>
|
91
|
+
struct Conversion<bool>
|
92
|
+
{
|
93
|
+
static bool from(RubyValue value) { return value; }
|
94
|
+
static RubyValue to(bool x) { return x ? Qtrue : Qfalse; }
|
95
|
+
};
|
96
|
+
|
97
|
+
// signed integers
|
98
|
+
|
99
|
+
template <typename T>
|
100
|
+
struct Conversion<T, typename std::enable_if<std::is_signed<T>::value && std::is_integral<T>::value>::type>
|
101
|
+
{
|
102
|
+
static T from(RubyValue x)
|
103
|
+
{
|
104
|
+
return protect([&] {
|
105
|
+
return NUM2LL(x);
|
106
|
+
});
|
107
|
+
}
|
108
|
+
static RubyValue to(T x)
|
109
|
+
{
|
110
|
+
return LL2NUM(x);
|
111
|
+
}
|
112
|
+
};
|
113
|
+
|
114
|
+
// unsigned integers
|
115
|
+
|
116
|
+
template <typename T>
|
117
|
+
struct Conversion<T, typename std::enable_if<std::is_unsigned<T>::value && std::is_integral<T>::value>::type>
|
118
|
+
{
|
119
|
+
static T from(RubyValue x)
|
120
|
+
{
|
121
|
+
return protect([&] {
|
122
|
+
return NUM2ULL(x);
|
123
|
+
});
|
124
|
+
}
|
125
|
+
static RubyValue to(T x)
|
126
|
+
{
|
127
|
+
return ULL2NUM(x);
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
// floating point values
|
132
|
+
|
133
|
+
template <typename T>
|
134
|
+
struct Conversion<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
|
135
|
+
{
|
136
|
+
static T from(RubyValue x)
|
137
|
+
{
|
138
|
+
return protect([&] {
|
139
|
+
auto type = rb_type(x);
|
140
|
+
if (type == T_FIXNUM || type == T_BIGNUM) {
|
141
|
+
return double(NUM2LL(x));
|
142
|
+
} else {
|
143
|
+
return rb_float_value(x);
|
144
|
+
}
|
145
|
+
});
|
146
|
+
}
|
147
|
+
static RubyValue to(T x)
|
148
|
+
{
|
149
|
+
return rb_float_new(x);
|
150
|
+
}
|
151
|
+
};
|
152
|
+
|
153
|
+
// QList and QVector
|
154
|
+
|
155
|
+
template <class T> struct IsQListLike : std::false_type {};
|
156
|
+
template <class V> struct IsQListLike<QList<V>> : std::true_type {};
|
157
|
+
template <class V> struct IsQListLike<QVector<V>> : std::true_type {};
|
158
|
+
|
159
|
+
template <template<class> class T, class V>
|
160
|
+
struct Conversion<T<V>, typename std::enable_if<IsQListLike<T<V>>::value>::type>
|
161
|
+
{
|
162
|
+
static T<V> from(RubyValue x)
|
163
|
+
{
|
164
|
+
protect([&] {
|
165
|
+
x = rb_convert_type(x, T_ARRAY, "Array", "to_ary");
|
166
|
+
});
|
167
|
+
int length = RARRAY_LEN(VALUE(x));
|
168
|
+
T<V> list;
|
169
|
+
list.reserve(length);
|
170
|
+
for (int i = 0; i < length; ++i) {
|
171
|
+
list << RubyValue(RARRAY_AREF(VALUE(x), i)).to<V>();
|
172
|
+
}
|
173
|
+
return list;
|
174
|
+
}
|
175
|
+
static RubyValue to(const T<V> &list)
|
176
|
+
{
|
177
|
+
RubyValue ary = protect([&] {
|
178
|
+
return rb_ary_new();
|
179
|
+
});
|
180
|
+
for (const auto &elem : list) {
|
181
|
+
auto x = RubyValue::from(elem);
|
182
|
+
protect([&] {
|
183
|
+
rb_ary_push(ary, x);
|
184
|
+
});
|
185
|
+
}
|
186
|
+
return ary;
|
187
|
+
}
|
188
|
+
};
|
189
|
+
|
190
|
+
// QHash and QMap
|
191
|
+
|
192
|
+
template <class T> struct IsQHashLike : std::false_type {};
|
193
|
+
template <class K, class V> struct IsQHashLike<QHash<K, V>> : std::true_type {};
|
194
|
+
template <class K, class V> struct IsQHashLike<QMap<K, V>> : std::true_type {};
|
195
|
+
|
196
|
+
template <template<class, class> class T, class K, class V>
|
197
|
+
struct Conversion<T<K, V>, typename std::enable_if<IsQHashLike<T<K, V>>::value>::type>
|
198
|
+
{
|
199
|
+
static T<K, V> from(RubyValue x)
|
200
|
+
{
|
201
|
+
protect([&] {
|
202
|
+
x = rb_convert_type(x, T_HASH, "Hash", "to_hash");
|
203
|
+
});
|
204
|
+
T<K, V> hash;
|
205
|
+
protect([&] {
|
206
|
+
auto each = [](VALUE key, VALUE value, VALUE arg) -> int {
|
207
|
+
auto &hash = *reinterpret_cast<T<K, V> *>(arg);
|
208
|
+
unprotect([&] {
|
209
|
+
hash[RubyValue(key).to<K>()] = RubyValue(value).to<V>();
|
210
|
+
});
|
211
|
+
return ST_CONTINUE;
|
212
|
+
};
|
213
|
+
auto eachPtr = (int (*)(VALUE, VALUE, VALUE))each;
|
214
|
+
rb_hash_foreach(x, (int (*)(...))eachPtr, (VALUE)(&hash));
|
215
|
+
});
|
216
|
+
return hash;
|
217
|
+
}
|
218
|
+
static RubyValue to(const T<K, V> &hash)
|
219
|
+
{
|
220
|
+
RubyValue rubyHash = protect([&] {
|
221
|
+
return rb_hash_new();
|
222
|
+
});
|
223
|
+
for (auto i = hash.begin(); i != hash.end(); ++i) {
|
224
|
+
auto k = RubyValue::from(i.key());
|
225
|
+
auto v = RubyValue::from(i.value());
|
226
|
+
protect([&] {
|
227
|
+
rb_hash_aset(rubyHash, k, v);
|
228
|
+
});
|
229
|
+
}
|
230
|
+
return rubyHash;
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
// QObject-derived
|
235
|
+
|
236
|
+
template <typename T>
|
237
|
+
struct Conversion<
|
238
|
+
T *,
|
239
|
+
typename std::enable_if<
|
240
|
+
std::is_base_of<QObject, T>::value
|
241
|
+
>::type
|
242
|
+
>
|
243
|
+
{
|
244
|
+
static T *from(RubyValue value) { return dynamic_cast<T *>(value.toQObject()); }
|
245
|
+
static RubyValue to(T *value) { return RubyValue::fromQObject(value); }
|
246
|
+
};
|
247
|
+
|
248
|
+
template <>
|
249
|
+
struct Conversion<const char *>
|
250
|
+
{
|
251
|
+
static RubyValue to(const char *str);
|
252
|
+
};
|
253
|
+
|
254
|
+
} // namespace detail
|
255
|
+
|
256
|
+
} // namespace RubyQml
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#include "signalforwarder.h"
|
2
|
+
#include "util.h"
|
3
|
+
#include <QtCore/QDebug>
|
4
|
+
|
5
|
+
namespace RubyQml {
|
6
|
+
|
7
|
+
SignalForwarder::SignalForwarder(QObject *obj, const QMetaMethod &signal, RubyValue proc) :
|
8
|
+
QObject(obj),
|
9
|
+
mSignal(signal),
|
10
|
+
mProcRef(proc)
|
11
|
+
{
|
12
|
+
QMetaObject::connect(obj, signal.methodIndex(), this, QObject::staticMetaObject.methodCount());
|
13
|
+
}
|
14
|
+
|
15
|
+
SignalForwarder::~SignalForwarder()
|
16
|
+
{
|
17
|
+
}
|
18
|
+
|
19
|
+
int SignalForwarder::qt_metacall(QMetaObject::Call call, int id, void **args)
|
20
|
+
{
|
21
|
+
id = QObject::qt_metacall(call, id, args);
|
22
|
+
if (id < 0) {
|
23
|
+
return id;
|
24
|
+
}
|
25
|
+
if (call == QMetaObject::InvokeMetaMethod) {
|
26
|
+
if (id == 0) {
|
27
|
+
forwardArgs(args);
|
28
|
+
}
|
29
|
+
--id;
|
30
|
+
}
|
31
|
+
return id;
|
32
|
+
}
|
33
|
+
|
34
|
+
void SignalForwarder::gc_mark()
|
35
|
+
{
|
36
|
+
rb_gc_mark(mProcRef.value());
|
37
|
+
}
|
38
|
+
|
39
|
+
void SignalForwarder::forwardArgs(void **args)
|
40
|
+
{
|
41
|
+
QVariantList list;
|
42
|
+
list.reserve(mSignal.parameterCount());
|
43
|
+
for (int i = 0; i < mSignal.parameterCount(); ++i) {
|
44
|
+
auto type = mSignal.parameterType(i);
|
45
|
+
if (type == QMetaType::QVariant) {
|
46
|
+
list << *static_cast<QVariant *>(args[i + 1]);
|
47
|
+
} else {
|
48
|
+
list << QVariant(type, args[i + 1]);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
callProc(list);
|
52
|
+
}
|
53
|
+
|
54
|
+
void SignalForwarder::callProc(const QVariantList &list)
|
55
|
+
{
|
56
|
+
if (mProcRef.hasValue()) {
|
57
|
+
withGvl([&] {
|
58
|
+
auto args = RubyValue::from(list);
|
59
|
+
protect([&] {
|
60
|
+
rb_funcall2(mProcRef.value(), rb_intern("call"), RARRAY_LEN(VALUE(args)), RARRAY_PTR(VALUE(args)));
|
61
|
+
});
|
62
|
+
});
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
} // namespace RubyQml
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "rubyvalue.h"
|
3
|
+
#include "weakvaluereference.h"
|
4
|
+
#include "markable.h"
|
5
|
+
#include <QObject>
|
6
|
+
#include <QMetaMethod>
|
7
|
+
|
8
|
+
namespace RubyQml {
|
9
|
+
|
10
|
+
class SignalForwarder : public QObject, public Markable
|
11
|
+
{
|
12
|
+
public:
|
13
|
+
SignalForwarder(QObject *obj, const QMetaMethod &signal, RubyValue proc);
|
14
|
+
~SignalForwarder();
|
15
|
+
|
16
|
+
int qt_metacall(QMetaObject::Call call, int id, void **args) override;
|
17
|
+
|
18
|
+
void gc_mark() override;
|
19
|
+
|
20
|
+
private:
|
21
|
+
void forwardArgs(void **args);
|
22
|
+
void callProc(const QVariantList &list);
|
23
|
+
|
24
|
+
QMetaMethod mSignal;
|
25
|
+
WeakValueReference mProcRef;
|
26
|
+
};
|
27
|
+
|
28
|
+
} // namespace RubyQml
|
29
|
+
|
data/ext/qml/util.cpp
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
#include "util.h"
|
2
|
+
#include "rubyvalue.h"
|
3
|
+
#include <QString>
|
4
|
+
#include <ruby/thread.h>
|
5
|
+
#include <string>
|
6
|
+
#include <memory>
|
7
|
+
#include <cxxabi.h>
|
8
|
+
|
9
|
+
namespace RubyQml {
|
10
|
+
|
11
|
+
void protect(const std::function<void ()> &doAction)
|
12
|
+
{
|
13
|
+
auto callback = [](VALUE data) {
|
14
|
+
auto &doAction= *reinterpret_cast<const std::function<void ()> *>(data);
|
15
|
+
doAction();
|
16
|
+
return Qnil;
|
17
|
+
};
|
18
|
+
int state;
|
19
|
+
rb_protect(callback, reinterpret_cast<VALUE>(&doAction), &state);
|
20
|
+
if (state) {
|
21
|
+
throw RubyException(state);
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
void unprotect(const std::function<void ()> &doAction) noexcept
|
26
|
+
{
|
27
|
+
int state = 0;
|
28
|
+
bool cppErrorOccured= false;
|
29
|
+
VALUE cppErrorClassName = Qnil;
|
30
|
+
VALUE cppErrorMessage = Qnil;
|
31
|
+
try {
|
32
|
+
doAction();
|
33
|
+
}
|
34
|
+
catch (const RubyException &ex) {
|
35
|
+
state = ex.state();
|
36
|
+
}
|
37
|
+
catch (const std::exception &ex) {
|
38
|
+
cppErrorOccured = true;
|
39
|
+
int status;
|
40
|
+
auto classname = abi::__cxa_demangle(typeid(ex).name(), nullptr, nullptr, &status);
|
41
|
+
cppErrorClassName = rb_str_new_cstr(classname);
|
42
|
+
free(classname);
|
43
|
+
cppErrorMessage = rb_str_new_cstr(ex.what());
|
44
|
+
}
|
45
|
+
if (state) {
|
46
|
+
rb_jump_tag(state);
|
47
|
+
}
|
48
|
+
if (cppErrorOccured) {
|
49
|
+
auto patterns = rb_funcall(rb_path2class("QML::ErrorConverter"), rb_intern("patterns"), 0);
|
50
|
+
auto rubyClass = rb_hash_aref(patterns, cppErrorClassName);
|
51
|
+
VALUE exc;
|
52
|
+
if (RTEST(rubyClass)) {
|
53
|
+
exc = rb_funcall(rubyClass, rb_intern("new"), 1, cppErrorMessage);
|
54
|
+
} else {
|
55
|
+
exc = rb_funcall(rb_path2class("QML::CppError"), rb_intern("new"), 2, cppErrorClassName, cppErrorMessage);
|
56
|
+
}
|
57
|
+
rb_exc_raise(exc);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
void rescue(const std::function<void ()> &doAction, const std::function<void (RubyValue)> &handleException)
|
62
|
+
{
|
63
|
+
VALUE (*callback)(VALUE) = [](VALUE data) {
|
64
|
+
(*reinterpret_cast<std::function<void ()> *>(data))();
|
65
|
+
return Qnil;
|
66
|
+
};
|
67
|
+
VALUE (*rescueCallback)(VALUE, VALUE) = [](VALUE data, VALUE excObject) {
|
68
|
+
(*reinterpret_cast<std::function<void (VALUE)>*>(data))(excObject);
|
69
|
+
return Qnil;
|
70
|
+
};
|
71
|
+
rb_rescue((VALUE (*)(...))callback, (VALUE)&doAction, (VALUE (*)(...))rescueCallback, (VALUE)&handleException);
|
72
|
+
}
|
73
|
+
|
74
|
+
namespace {
|
75
|
+
|
76
|
+
void changeGvl(const std::function<void ()> &doAction, bool gvl)
|
77
|
+
{
|
78
|
+
auto actionPtr = const_cast<void *>(static_cast<const void *>(&doAction));
|
79
|
+
auto f = [](void *data) -> void * {
|
80
|
+
auto &doAction= *static_cast<const std::function<void ()> *>(data);
|
81
|
+
try {
|
82
|
+
doAction();
|
83
|
+
} catch (...) {
|
84
|
+
return new std::exception_ptr(std::current_exception());
|
85
|
+
}
|
86
|
+
return nullptr;
|
87
|
+
};
|
88
|
+
void *result;
|
89
|
+
if (gvl) {
|
90
|
+
result = rb_thread_call_with_gvl(f, actionPtr );
|
91
|
+
} else {
|
92
|
+
result = rb_thread_call_without_gvl(f, actionPtr , RUBY_UBF_IO, nullptr);
|
93
|
+
}
|
94
|
+
std::unique_ptr<std::exception_ptr> exc(static_cast<std::exception_ptr *>(result));
|
95
|
+
if (exc && *exc) {
|
96
|
+
std::rethrow_exception(*exc);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
}
|
101
|
+
|
102
|
+
void withoutGvl(const std::function<void ()> &doAction)
|
103
|
+
{
|
104
|
+
changeGvl(doAction, false);
|
105
|
+
}
|
106
|
+
|
107
|
+
void withGvl(const std::function<void ()> &doAction)
|
108
|
+
{
|
109
|
+
changeGvl(doAction, true);
|
110
|
+
}
|
111
|
+
|
112
|
+
void fail(const char *errorClassName, const QString &message)
|
113
|
+
{
|
114
|
+
auto msg = message.toUtf8();
|
115
|
+
protect([&] {
|
116
|
+
rb_raise(rb_path2class(errorClassName), "%s", msg.data());
|
117
|
+
});
|
118
|
+
}
|
119
|
+
|
120
|
+
}
|