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
@@ -0,0 +1,32 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include <QObject>
|
3
|
+
#include <QVariant>
|
4
|
+
|
5
|
+
namespace RubyQml {
|
6
|
+
|
7
|
+
class TestObject;
|
8
|
+
class TestObjectSubclass;
|
9
|
+
class OwnershipTest;
|
10
|
+
class ObjectLifeChecker;
|
11
|
+
|
12
|
+
class TestUtilPlugin : public QObject
|
13
|
+
{
|
14
|
+
Q_OBJECT
|
15
|
+
Q_PLUGIN_METADATA(IID "org.ruby-qml.RubyQml.TestObject")
|
16
|
+
public:
|
17
|
+
explicit TestUtilPlugin(QObject *parent = 0);
|
18
|
+
signals:
|
19
|
+
|
20
|
+
public slots:
|
21
|
+
QVariantHash metaObjects() { return mMetaObjects; }
|
22
|
+
|
23
|
+
RubyQml::TestObject *createTestObject();
|
24
|
+
RubyQml::TestObjectSubclass *createTestObjectSubclass();
|
25
|
+
RubyQml::OwnershipTest *createOwnershipTest();
|
26
|
+
RubyQml::ObjectLifeChecker *createObjectLifeChecker(QObject *target);
|
27
|
+
|
28
|
+
private:
|
29
|
+
QVariantHash mMetaObjects;
|
30
|
+
};
|
31
|
+
|
32
|
+
} // namespace RubyQml
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#include "qmltyperegisterer.h"
|
2
|
+
#include "foreignmetaobject.h"
|
3
|
+
#include "accessobject.h"
|
4
|
+
#include <QtQml>
|
5
|
+
|
6
|
+
namespace RubyQml {
|
7
|
+
|
8
|
+
QmlTypeRegisterer::QmlTypeRegisterer(const SP<ForeignMetaObject> &metaObject, const std::function<void (void *)> &createFunc) :
|
9
|
+
mMetaObject(metaObject),
|
10
|
+
mCreateFunc(createFunc)
|
11
|
+
{
|
12
|
+
mFactoryArgs[0] = &ffi_type_pointer;
|
13
|
+
if (ffi_prep_cif(&mFactoryCif, FFI_DEFAULT_ABI, 1, &ffi_type_void, mFactoryArgs) != FFI_OK) {
|
14
|
+
throw std::runtime_error("failed to prepare FFI call interface");
|
15
|
+
}
|
16
|
+
mFactoryClosure = (ffi_closure *)ffi_closure_alloc(sizeof(ffi_closure), (void **)(&mFactoryFunc));
|
17
|
+
if (!mFactoryClosure) {
|
18
|
+
throw std::runtime_error("failed to allocate FFI closure");
|
19
|
+
}
|
20
|
+
|
21
|
+
auto callback = [](ffi_cif *cif, void *ret, void **args, void *data) {
|
22
|
+
Q_UNUSED(cif);
|
23
|
+
Q_UNUSED(ret);
|
24
|
+
auto self = (QmlTypeRegisterer *)(data);
|
25
|
+
self->mCreateFunc(*(void **)(args[0]));
|
26
|
+
};
|
27
|
+
|
28
|
+
if (ffi_prep_closure_loc(mFactoryClosure, &mFactoryCif, callback, this, (void *)mFactoryFunc) != FFI_OK) {
|
29
|
+
ffi_closure_free(mFactoryClosure);
|
30
|
+
throw std::runtime_error("failed to prepare FFI closure");
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
QmlTypeRegisterer::~QmlTypeRegisterer()
|
35
|
+
{
|
36
|
+
ffi_closure_free(mFactoryClosure);
|
37
|
+
}
|
38
|
+
|
39
|
+
void QmlTypeRegisterer::registerType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
|
40
|
+
{
|
41
|
+
QByteArray className;
|
42
|
+
className += mMetaObject->className();
|
43
|
+
className += "*";
|
44
|
+
|
45
|
+
QByteArray listName;
|
46
|
+
listName += "QQmlListProperty<";
|
47
|
+
listName += mMetaObject->className();
|
48
|
+
listName += ">";
|
49
|
+
|
50
|
+
QQmlPrivate::RegisterType type = {
|
51
|
+
0,
|
52
|
+
qRegisterNormalizedMetaType<AccessWrapper *>(className),
|
53
|
+
qRegisterNormalizedMetaType<QQmlListProperty<AccessWrapper> >(listName),
|
54
|
+
sizeof(AccessWrapper), mFactoryFunc,
|
55
|
+
QString(),
|
56
|
+
|
57
|
+
uri, versionMajor, versionMinor, qmlName, mMetaObject.get(),
|
58
|
+
|
59
|
+
0, 0,
|
60
|
+
|
61
|
+
QQmlPrivate::StaticCastSelector<AccessWrapper,QQmlParserStatus>::cast(),
|
62
|
+
QQmlPrivate::StaticCastSelector<AccessWrapper,QQmlPropertyValueSource>::cast(),
|
63
|
+
QQmlPrivate::StaticCastSelector<AccessWrapper,QQmlPropertyValueInterceptor>::cast(),
|
64
|
+
|
65
|
+
0, 0,
|
66
|
+
|
67
|
+
0,
|
68
|
+
0
|
69
|
+
};
|
70
|
+
|
71
|
+
QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
|
72
|
+
}
|
73
|
+
|
74
|
+
} // namespace RubyQml
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "common.h"
|
3
|
+
#include <ffi.h>
|
4
|
+
#include <functional>
|
5
|
+
|
6
|
+
namespace RubyQml {
|
7
|
+
|
8
|
+
class ForeignMetaObject;
|
9
|
+
|
10
|
+
class QmlTypeRegisterer
|
11
|
+
{
|
12
|
+
public:
|
13
|
+
using FactoryFunction = void (*)(void *);
|
14
|
+
|
15
|
+
QmlTypeRegisterer(const SP<ForeignMetaObject> &metaObject, const std::function<void(void *)> &createFunc);
|
16
|
+
~QmlTypeRegisterer();
|
17
|
+
|
18
|
+
void registerType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
|
19
|
+
|
20
|
+
private:
|
21
|
+
|
22
|
+
SP<ForeignMetaObject> mMetaObject;
|
23
|
+
std::function<void (void *)> mCreateFunc;
|
24
|
+
ffi_type *mFactoryArgs[1];
|
25
|
+
ffi_cif mFactoryCif;
|
26
|
+
ffi_closure *mFactoryClosure = nullptr;
|
27
|
+
FactoryFunction mFactoryFunc = nullptr;
|
28
|
+
};
|
29
|
+
|
30
|
+
} // namespace RubyQml
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#include "rubyclass.h"
|
2
|
+
|
3
|
+
namespace RubyQml {
|
4
|
+
|
5
|
+
RubyModule::RubyModule(VALUE moduleValue) :
|
6
|
+
RubyModule(RubyValue(moduleValue))
|
7
|
+
{
|
8
|
+
}
|
9
|
+
|
10
|
+
RubyModule::RubyModule(RubyValue moduleValue) :
|
11
|
+
mValue(moduleValue)
|
12
|
+
{
|
13
|
+
checkType();
|
14
|
+
}
|
15
|
+
|
16
|
+
RubyModule::RubyModule(const char *name)
|
17
|
+
{
|
18
|
+
protect([&] {
|
19
|
+
mValue = rb_define_module(name);
|
20
|
+
});
|
21
|
+
}
|
22
|
+
|
23
|
+
RubyModule::RubyModule(const RubyModule &under, const char *name)
|
24
|
+
{
|
25
|
+
protect([&] {
|
26
|
+
mValue = rb_define_module_under(under.toValue(), name);
|
27
|
+
});
|
28
|
+
}
|
29
|
+
|
30
|
+
RubyModule &RubyModule::operator=(const RubyModule &other)
|
31
|
+
{
|
32
|
+
mValue = other.mValue;
|
33
|
+
checkType();
|
34
|
+
return *this;
|
35
|
+
}
|
36
|
+
|
37
|
+
RubyModule RubyModule::fromPath(const char *path)
|
38
|
+
{
|
39
|
+
RubyValue ret;
|
40
|
+
protect([&] {
|
41
|
+
ret = rb_path2class(path);
|
42
|
+
});
|
43
|
+
return ret;
|
44
|
+
}
|
45
|
+
|
46
|
+
void RubyModule::aliasMethod(const char *newName, const char *originalName)
|
47
|
+
{
|
48
|
+
protect([&] {
|
49
|
+
rb_alias(mValue, rb_intern(newName), rb_intern(originalName));
|
50
|
+
});
|
51
|
+
}
|
52
|
+
|
53
|
+
void RubyModule::checkType()
|
54
|
+
{
|
55
|
+
if (!protect([&] { return rb_obj_is_kind_of(mValue, rb_cModule); })) {
|
56
|
+
throw std::logic_error("expected Module value");
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
RubyClass::RubyClass(RubyValue classValue) :
|
61
|
+
RubyModule(classValue)
|
62
|
+
{
|
63
|
+
checkType();
|
64
|
+
}
|
65
|
+
|
66
|
+
namespace {
|
67
|
+
|
68
|
+
RubyValue defineClass(const RubyModule &under, const char *name)
|
69
|
+
{
|
70
|
+
return protect([&] {
|
71
|
+
return rb_define_class_under(under.toValue(), name, rb_cObject);
|
72
|
+
});
|
73
|
+
}
|
74
|
+
|
75
|
+
}
|
76
|
+
|
77
|
+
RubyClass::RubyClass(const RubyModule &under, const char *name) :
|
78
|
+
RubyModule(defineClass(under, name))
|
79
|
+
{
|
80
|
+
}
|
81
|
+
|
82
|
+
RubyClass RubyClass::fromPath(const char *path)
|
83
|
+
{
|
84
|
+
return RubyClass(RubyModule::fromPath(path).toValue());
|
85
|
+
}
|
86
|
+
|
87
|
+
void RubyClass::checkType()
|
88
|
+
{
|
89
|
+
if (!toValue().isKindOf(rb_cClass)) {
|
90
|
+
throw std::logic_error("expected Class value");
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
} // namespace RubyQml
|
data/ext/qml/rubyclass.h
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
#pragma once
|
2
|
+
#include "functioninfo.h"
|
3
|
+
#include "rubyvalue.h"
|
4
|
+
#include <QDebug>
|
5
|
+
#include <memory>
|
6
|
+
|
7
|
+
namespace RubyQml {
|
8
|
+
|
9
|
+
enum class MethodAccess { Public, Protected, Private };
|
10
|
+
enum class MethodType { InstanceMethod, ModuleFunction };
|
11
|
+
|
12
|
+
class RubyModule
|
13
|
+
{
|
14
|
+
public:
|
15
|
+
RubyModule() = default;
|
16
|
+
RubyModule(VALUE moduleValue);
|
17
|
+
RubyModule(RubyValue moduleValue);
|
18
|
+
RubyModule(const char *name);
|
19
|
+
RubyModule(const RubyModule &under, const char *name);
|
20
|
+
RubyModule(const RubyModule &) = default;
|
21
|
+
RubyModule(RubyModule &&) = default;
|
22
|
+
RubyModule &operator=(const RubyModule &other);
|
23
|
+
|
24
|
+
static RubyModule fromPath(const char *path);
|
25
|
+
|
26
|
+
template <typename TFunction, TFunction function>
|
27
|
+
void defineMethod(MethodType type, MethodAccess access, const char *name, FunctionInfo<TFunction, function>)
|
28
|
+
{
|
29
|
+
using wrapper = FunctionWrapper<TFunction, function>;
|
30
|
+
auto func = (VALUE (*)(...))wrapper::apply;
|
31
|
+
auto argc = wrapper::argc - 1;
|
32
|
+
|
33
|
+
protect([&] {
|
34
|
+
switch (type) {
|
35
|
+
case MethodType::InstanceMethod:
|
36
|
+
switch (access) {
|
37
|
+
case MethodAccess::Public:
|
38
|
+
rb_define_method(mValue , name, func, argc);
|
39
|
+
break;
|
40
|
+
case MethodAccess::Protected:
|
41
|
+
rb_define_protected_method(mValue, name, func, argc);
|
42
|
+
break;
|
43
|
+
case MethodAccess::Private:
|
44
|
+
rb_define_private_method(mValue, name, func, argc);
|
45
|
+
break;
|
46
|
+
}
|
47
|
+
break;
|
48
|
+
case MethodType::ModuleFunction:
|
49
|
+
rb_define_module_function(mValue, name, func, argc);
|
50
|
+
break;
|
51
|
+
}
|
52
|
+
});
|
53
|
+
}
|
54
|
+
|
55
|
+
template <typename T>
|
56
|
+
void defineMethod(MethodAccess access, const char *name, T info)
|
57
|
+
{
|
58
|
+
defineMethod(MethodType::InstanceMethod, access, name, info);
|
59
|
+
}
|
60
|
+
|
61
|
+
template <typename T>
|
62
|
+
void defineModuleFunction(const char *name, T info)
|
63
|
+
{
|
64
|
+
defineMethod(MethodType::ModuleFunction, MethodAccess::Public, name, info);
|
65
|
+
}
|
66
|
+
|
67
|
+
template <typename T>
|
68
|
+
void defineMethod(const char *name, T info)
|
69
|
+
{
|
70
|
+
defineMethod(MethodAccess::Public, name, info);
|
71
|
+
}
|
72
|
+
|
73
|
+
void aliasMethod(const char *newName, const char *originalName);
|
74
|
+
|
75
|
+
RubyValue toValue() const { return mValue; }
|
76
|
+
operator RubyValue() const { return toValue(); }
|
77
|
+
operator VALUE() const { return toValue(); }
|
78
|
+
|
79
|
+
virtual void checkType();
|
80
|
+
|
81
|
+
private:
|
82
|
+
template <typename TFunction, TFunction function>
|
83
|
+
struct FunctionWrapper;
|
84
|
+
|
85
|
+
template <typename ... TArgs, RubyValue (*function)(TArgs...)>
|
86
|
+
struct FunctionWrapper<RubyValue (*)(TArgs...), function>
|
87
|
+
{
|
88
|
+
static constexpr size_t argc = sizeof...(TArgs);
|
89
|
+
|
90
|
+
static VALUE apply(typename std::conditional<true, VALUE, TArgs>::type... args)
|
91
|
+
{
|
92
|
+
RubyValue ret;
|
93
|
+
// use tuple to avoid gcc 4.8's bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55914)
|
94
|
+
auto tuple = std::make_tuple(args...);
|
95
|
+
unprotect([&] {
|
96
|
+
ret = applyWithTuple(function, tuple);
|
97
|
+
});
|
98
|
+
return ret;
|
99
|
+
}
|
100
|
+
};
|
101
|
+
|
102
|
+
RubyValue mValue;
|
103
|
+
};
|
104
|
+
|
105
|
+
class RubyClass : public RubyModule
|
106
|
+
{
|
107
|
+
public:
|
108
|
+
RubyClass() = default;
|
109
|
+
RubyClass(RubyValue classValue);
|
110
|
+
RubyClass(const RubyModule &under, const char *name);
|
111
|
+
|
112
|
+
static RubyClass fromPath(const char *path);
|
113
|
+
|
114
|
+
void checkType() override;
|
115
|
+
|
116
|
+
template <typename ... Args>
|
117
|
+
RubyValue newInstance(Args ... args)
|
118
|
+
{
|
119
|
+
return toValue().send(RUBYQML_INTERN("new"), args...);
|
120
|
+
}
|
121
|
+
};
|
122
|
+
|
123
|
+
|
124
|
+
template <typename T>
|
125
|
+
class WrapperRubyClass : public RubyClass
|
126
|
+
{
|
127
|
+
public:
|
128
|
+
WrapperRubyClass(const RubyModule &under, const char *name) :
|
129
|
+
RubyClass(under, name)
|
130
|
+
{
|
131
|
+
if (mInstance) {
|
132
|
+
throw std::logic_error("class already defined");
|
133
|
+
}
|
134
|
+
mInstance.reset(new WrapperRubyClass(*this));
|
135
|
+
|
136
|
+
protect([&] {
|
137
|
+
rb_define_alloc_func(toValue(), &alloc);
|
138
|
+
});
|
139
|
+
}
|
140
|
+
|
141
|
+
T *unwrap(RubyValue value)
|
142
|
+
{
|
143
|
+
auto klass = this->toValue();
|
144
|
+
protect([&] {
|
145
|
+
if (!RTEST(rb_obj_is_kind_of(value, klass))) {
|
146
|
+
rb_raise(rb_eTypeError, "expected %s, got %s", rb_class2name(klass), rb_obj_classname(value));
|
147
|
+
}
|
148
|
+
});
|
149
|
+
T *ptr;
|
150
|
+
Data_Get_Struct(VALUE(value), T, ptr);
|
151
|
+
return ptr;
|
152
|
+
}
|
153
|
+
|
154
|
+
using RubyClass::defineMethod;
|
155
|
+
|
156
|
+
template <typename TMemberFunction, TMemberFunction memberFunction>
|
157
|
+
void defineMethod(MethodAccess access, const char *name, MemberFunctionInfo<TMemberFunction, memberFunction>)
|
158
|
+
{
|
159
|
+
using wrapper = MethodWrapper<TMemberFunction, memberFunction>;
|
160
|
+
defineMethod(access, name, RUBYQML_FUNCTION_INFO(&wrapper::apply));
|
161
|
+
}
|
162
|
+
|
163
|
+
template <typename TMemberFunction, TMemberFunction memberFunction>
|
164
|
+
void defineMethod(const char *name, MemberFunctionInfo<TMemberFunction, memberFunction> info)
|
165
|
+
{
|
166
|
+
defineMethod(MethodAccess::Public, name, info);
|
167
|
+
}
|
168
|
+
|
169
|
+
static WrapperRubyClass instance()
|
170
|
+
{
|
171
|
+
if (!mInstance) {
|
172
|
+
throw std::logic_error("class not yet defined");
|
173
|
+
}
|
174
|
+
return *mInstance;
|
175
|
+
}
|
176
|
+
|
177
|
+
private:
|
178
|
+
|
179
|
+
template <typename TMemberFunction, TMemberFunction memfn>
|
180
|
+
struct MethodWrapper;
|
181
|
+
|
182
|
+
template <typename ... TArgs, RubyValue (T::*memfn)(TArgs ...)>
|
183
|
+
struct MethodWrapper<RubyValue (T::*)(TArgs ...), memfn>
|
184
|
+
{
|
185
|
+
static RubyValue apply(RubyValue self, TArgs ... args)
|
186
|
+
{
|
187
|
+
return (instance().unwrap(self)->*memfn)(args ...);
|
188
|
+
}
|
189
|
+
};
|
190
|
+
|
191
|
+
template <typename ... TArgs, RubyValue (T::*memfn)(TArgs ...) const>
|
192
|
+
struct MethodWrapper<RubyValue (T::*)(TArgs ...) const, memfn>
|
193
|
+
{
|
194
|
+
static RubyValue apply(RubyValue self, TArgs... args)
|
195
|
+
{
|
196
|
+
return (instance().unwrap(self)->*memfn)(args ...);
|
197
|
+
}
|
198
|
+
};
|
199
|
+
|
200
|
+
static void mark(T *ptr) noexcept
|
201
|
+
{
|
202
|
+
ptr->gc_mark();
|
203
|
+
}
|
204
|
+
|
205
|
+
static void dealloc(T *ptr) noexcept
|
206
|
+
{
|
207
|
+
withoutGvl([&] {
|
208
|
+
ptr->~T();
|
209
|
+
});
|
210
|
+
ruby_xfree(ptr);
|
211
|
+
}
|
212
|
+
|
213
|
+
static VALUE alloc(VALUE klass) noexcept
|
214
|
+
{
|
215
|
+
auto ptr = ruby_xmalloc(sizeof(T));
|
216
|
+
auto self = Data_Wrap_Struct(klass, &mark, &dealloc, ptr);
|
217
|
+
new(ptr) T(self);
|
218
|
+
return self;
|
219
|
+
}
|
220
|
+
|
221
|
+
static std::unique_ptr<WrapperRubyClass> mInstance;
|
222
|
+
};
|
223
|
+
|
224
|
+
template <typename T>
|
225
|
+
std::unique_ptr<WrapperRubyClass<T>> WrapperRubyClass<T>::mInstance = nullptr;
|
226
|
+
|
227
|
+
template <typename T>
|
228
|
+
inline WrapperRubyClass<T> wrapperRubyClass()
|
229
|
+
{
|
230
|
+
return WrapperRubyClass<T>::instance();
|
231
|
+
}
|
232
|
+
|
233
|
+
} // namespace RubyQml
|
234
|
+
|