extpp 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/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +48 -0
- data/doc/text/news.md +5 -0
- data/ext/extpp/class.cpp +261 -0
- data/ext/extpp/extconf.rb +75 -0
- data/ext/extpp/function.cpp +28 -0
- data/ext/extpp/object.cpp +45 -0
- data/include/ruby.hpp +10 -0
- data/include/ruby/cast.hpp +74 -0
- data/include/ruby/class.hpp +25 -0
- data/include/ruby/object.hpp +96 -0
- data/include/ruby/type.hpp +12 -0
- data/lib/extpp.rb +36 -0
- data/lib/extpp/compiler.rb +69 -0
- data/lib/extpp/version.rb +3 -0
- data/test/fixtures/cast/cast.cpp +32 -0
- data/test/fixtures/cast/extconf.rb +4 -0
- data/test/fixtures/class/class.cpp +46 -0
- data/test/fixtures/class/extconf.rb +4 -0
- data/test/fixtures/object/extconf.rb +4 -0
- data/test/fixtures/object/object.cpp +78 -0
- data/test/helper.rb +9 -0
- data/test/run-test.rb +23 -0
- data/test/test-cast.rb +42 -0
- data/test/test-class.rb +47 -0
- data/test/test-object.rb +52 -0
- metadata +169 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
#include "function.hpp"
|
2
|
+
|
3
|
+
namespace rb {
|
4
|
+
FunctionWithoutArgument::FunctionWithoutArgument(const MethodWithoutArguments &function) :
|
5
|
+
function_(function) {
|
6
|
+
}
|
7
|
+
|
8
|
+
VALUE FunctionWithoutArgument::call(VALUE self, int argc, VALUE *argv) {
|
9
|
+
return function_(self);
|
10
|
+
};
|
11
|
+
|
12
|
+
|
13
|
+
FunctionWithArguments::FunctionWithArguments(const MethodWithArguments &function) :
|
14
|
+
function_(function) {
|
15
|
+
}
|
16
|
+
|
17
|
+
VALUE FunctionWithArguments::call(VALUE self, int argc, VALUE *argv) {
|
18
|
+
return function_(self, argc, argv);
|
19
|
+
};
|
20
|
+
|
21
|
+
FunctionWithArgumentsCompatible::FunctionWithArgumentsCompatible(const MethodWithArgumentsCompatible &function) :
|
22
|
+
function_(function) {
|
23
|
+
}
|
24
|
+
|
25
|
+
VALUE FunctionWithArgumentsCompatible::call(VALUE self, int argc, VALUE *argv) {
|
26
|
+
return function_(argc, argv, self);
|
27
|
+
};
|
28
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#include <ruby/object.hpp>
|
2
|
+
|
3
|
+
namespace {
|
4
|
+
VALUE call_block(RB_BLOCK_CALL_FUNC_ARGLIST(rb_data, rb_block)) {
|
5
|
+
auto block = reinterpret_cast<rb::MethodWithoutArguments>(rb_block);
|
6
|
+
return block(rb_data);
|
7
|
+
}
|
8
|
+
}
|
9
|
+
|
10
|
+
namespace rb {
|
11
|
+
Object Object::send(ID name_id) {
|
12
|
+
VALUE rb_result = rb_funcall(rb_object_, name_id, 0);
|
13
|
+
return Object(rb_result);
|
14
|
+
}
|
15
|
+
|
16
|
+
Object Object::send(ID name_id,
|
17
|
+
std::initializer_list<VALUE> args) {
|
18
|
+
size_t n = args.size();
|
19
|
+
VALUE rb_args[n];
|
20
|
+
int i = 0;
|
21
|
+
for (auto arg : args) {
|
22
|
+
rb_args[i++] = arg;
|
23
|
+
}
|
24
|
+
VALUE rb_result = rb_funcallv(rb_object_, name_id, n, rb_args);
|
25
|
+
return Object(rb_result);
|
26
|
+
}
|
27
|
+
|
28
|
+
Object Object::send(ID name_id,
|
29
|
+
std::initializer_list<VALUE> args,
|
30
|
+
MethodWithoutArguments block) {
|
31
|
+
size_t n = args.size();
|
32
|
+
VALUE rb_args[n];
|
33
|
+
int i = 0;
|
34
|
+
for (auto arg : args) {
|
35
|
+
rb_args[i++] = arg;
|
36
|
+
}
|
37
|
+
VALUE rb_result = rb_block_call(rb_object_,
|
38
|
+
name_id,
|
39
|
+
n,
|
40
|
+
rb_args,
|
41
|
+
reinterpret_cast<MethodFunc>(call_block),
|
42
|
+
reinterpret_cast<VALUE>(block));
|
43
|
+
return Object(rb_result);
|
44
|
+
}
|
45
|
+
}
|
data/include/ruby.hpp
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby/object.hpp>
|
4
|
+
|
5
|
+
namespace rb {
|
6
|
+
template <typename RETURN_TYPE, typename ARGUMENT_TYPE>
|
7
|
+
inline RETURN_TYPE cast(ARGUMENT_TYPE object);
|
8
|
+
|
9
|
+
template <typename RETURN_TYPE,
|
10
|
+
typename ARGUMENT_TYPE,
|
11
|
+
typename ADDTIONAL_DATA_TYPE1>
|
12
|
+
inline RETURN_TYPE cast(ARGUMENT_TYPE object,
|
13
|
+
ADDTIONAL_DATA_TYPE1 data1);
|
14
|
+
|
15
|
+
template <>
|
16
|
+
inline int32_t cast<int32_t, Object>(Object rb_object) {
|
17
|
+
return NUM2INT(rb_object);
|
18
|
+
}
|
19
|
+
|
20
|
+
template <>
|
21
|
+
inline Object cast<Object, int32_t>(int32_t n) {
|
22
|
+
return Object(INT2NUM(n));
|
23
|
+
}
|
24
|
+
|
25
|
+
|
26
|
+
template <>
|
27
|
+
inline int64_t cast<int64_t, Object>(Object rb_object) {
|
28
|
+
return NUM2LONG(rb_object);
|
29
|
+
}
|
30
|
+
|
31
|
+
template <>
|
32
|
+
inline Object cast<Object, int64_t>(int64_t n) {
|
33
|
+
return Object(LONG2NUM(n));
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
template <>
|
38
|
+
inline uint32_t cast<uint32_t, Object>(Object rb_object) {
|
39
|
+
return NUM2UINT(rb_object);
|
40
|
+
}
|
41
|
+
|
42
|
+
template <>
|
43
|
+
inline Object cast<Object, uint32_t>(uint32_t n) {
|
44
|
+
return Object(UINT2NUM(n));
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
template <>
|
49
|
+
inline uint64_t cast<uint64_t, Object>(Object rb_object) {
|
50
|
+
return NUM2ULONG(rb_object);
|
51
|
+
}
|
52
|
+
|
53
|
+
template <>
|
54
|
+
inline Object cast<Object, uint64_t>(uint64_t n) {
|
55
|
+
return Object(ULONG2NUM(n));
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
template <>
|
60
|
+
inline const char *cast<const char *, Object>(Object rb_object) {
|
61
|
+
VALUE rb_object_raw = rb_object;
|
62
|
+
return StringValueCStr(rb_object_raw);
|
63
|
+
}
|
64
|
+
|
65
|
+
template <>
|
66
|
+
inline Object cast<Object, const char *>(const char *c_string) {
|
67
|
+
return Object(rb_str_new_cstr(c_string));
|
68
|
+
}
|
69
|
+
|
70
|
+
template <>
|
71
|
+
inline Object cast<Object, const char *, long>(const char *data, long size) {
|
72
|
+
return Object(rb_str_new(data, size));
|
73
|
+
}
|
74
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby/object.hpp>
|
4
|
+
|
5
|
+
namespace rb {
|
6
|
+
class Class: public Object {
|
7
|
+
public:
|
8
|
+
Class(const char *name, VALUE parent=rb_cObject);
|
9
|
+
Class(VALUE klass);
|
10
|
+
~Class();
|
11
|
+
|
12
|
+
Class &define_method(const char *name,
|
13
|
+
MethodWithoutArguments body);
|
14
|
+
Class &define_method(const char *name,
|
15
|
+
MethodWithArguments body);
|
16
|
+
Class &define_method(const char *name,
|
17
|
+
MethodWithArgumentsCompatible body);
|
18
|
+
|
19
|
+
Class &enable_lazy_define_method();
|
20
|
+
|
21
|
+
private:
|
22
|
+
class ClassImpl;
|
23
|
+
ClassImpl *impl_;
|
24
|
+
};
|
25
|
+
}
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby/type.hpp>
|
4
|
+
|
5
|
+
#include <initializer_list>
|
6
|
+
|
7
|
+
namespace rb {
|
8
|
+
class Object {
|
9
|
+
public:
|
10
|
+
explicit Object(VALUE rb_object=Qnil) :
|
11
|
+
rb_object_(rb_object),
|
12
|
+
is_gc_guarding_(false) {
|
13
|
+
}
|
14
|
+
|
15
|
+
explicit Object(const char *name) :
|
16
|
+
rb_object_(rb_const_get(rb_cObject, rb_intern(name))),
|
17
|
+
is_gc_guarding_(false) {
|
18
|
+
}
|
19
|
+
|
20
|
+
Object(Object const &object) :
|
21
|
+
rb_object_(object),
|
22
|
+
is_gc_guarding_(false) {
|
23
|
+
}
|
24
|
+
|
25
|
+
virtual ~Object() {
|
26
|
+
if (is_gc_guarding_) {
|
27
|
+
rb_gc_unregister_address(&rb_object_);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
inline explicit operator bool() const {
|
32
|
+
return RTEST(rb_object_);
|
33
|
+
}
|
34
|
+
|
35
|
+
inline VALUE to_ruby() const {
|
36
|
+
return rb_object_;
|
37
|
+
}
|
38
|
+
|
39
|
+
inline operator VALUE() const {
|
40
|
+
return rb_object_;
|
41
|
+
}
|
42
|
+
|
43
|
+
inline bool is_nil() const {
|
44
|
+
return NIL_P(rb_object_);
|
45
|
+
}
|
46
|
+
|
47
|
+
inline void guard_from_gc() {
|
48
|
+
if (!is_gc_guarding_) {
|
49
|
+
return;
|
50
|
+
}
|
51
|
+
|
52
|
+
is_gc_guarding_ = true;
|
53
|
+
rb_gc_register_address(&rb_object_);
|
54
|
+
}
|
55
|
+
|
56
|
+
Object send(ID name_id);
|
57
|
+
|
58
|
+
inline Object send(const char *name) {
|
59
|
+
return send(rb_intern(name));
|
60
|
+
}
|
61
|
+
|
62
|
+
inline Object send(Object name) {
|
63
|
+
return send(rb_intern_str(name));
|
64
|
+
}
|
65
|
+
|
66
|
+
Object send(ID name_id, std::initializer_list<VALUE> args);
|
67
|
+
|
68
|
+
inline Object send(const char *name, std::initializer_list<VALUE> args) {
|
69
|
+
return send(rb_intern(name), args);
|
70
|
+
}
|
71
|
+
|
72
|
+
inline Object send(Object name, std::initializer_list<VALUE> args) {
|
73
|
+
return send(rb_intern_str(name), args);
|
74
|
+
}
|
75
|
+
|
76
|
+
Object send(ID name_id,
|
77
|
+
std::initializer_list<VALUE> args,
|
78
|
+
MethodWithoutArguments block);
|
79
|
+
|
80
|
+
inline Object send(const char *name,
|
81
|
+
std::initializer_list<VALUE> args,
|
82
|
+
MethodWithoutArguments block) {
|
83
|
+
return send(rb_intern(name), args, block);
|
84
|
+
}
|
85
|
+
|
86
|
+
inline Object send(Object name,
|
87
|
+
std::initializer_list<VALUE> args,
|
88
|
+
MethodWithoutArguments block) {
|
89
|
+
return send(rb_intern_str(name), args, block);
|
90
|
+
}
|
91
|
+
|
92
|
+
private:
|
93
|
+
VALUE rb_object_;
|
94
|
+
bool is_gc_guarding_;
|
95
|
+
};
|
96
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <ruby.h>
|
4
|
+
|
5
|
+
namespace rb {
|
6
|
+
using MethodFunc = VALUE (*)(ANYARGS);
|
7
|
+
|
8
|
+
using MethodWithoutArguments = VALUE (*)(VALUE self);
|
9
|
+
using MethodWithArguments = VALUE (*)(VALUE self, int argc, VALUE *argv);
|
10
|
+
using MethodWithArgumentsCompatible =
|
11
|
+
VALUE (*)(int argc, VALUE *argv, VALUE self);
|
12
|
+
}
|
data/lib/extpp.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "extpp/compiler"
|
2
|
+
|
3
|
+
compiler = Extpp::Compiler.new($CXXFLAGS)
|
4
|
+
compiler.check
|
5
|
+
$CXXFLAGS = compiler.cxx_flags
|
6
|
+
|
7
|
+
include_path = File.expand_path(File.join(__dir__, "..", "include"))
|
8
|
+
$INCFLAGS += " -I#{include_path.quote}"
|
9
|
+
|
10
|
+
header_files = Dir.chdir(include_path) do
|
11
|
+
Dir.glob("**/*.*").collect do |path|
|
12
|
+
File.join(include_path, path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
header_files_dependency_injector = Module.new do
|
16
|
+
define_method(:create_makefile) do |*args, &block|
|
17
|
+
super(*args, &block)
|
18
|
+
File.open("Makefile", "ab") do |makefile|
|
19
|
+
makefile.print <<-MAKEFILE
|
20
|
+
|
21
|
+
extpp_headers = #{header_files.join(" ")}
|
22
|
+
$(OBJS): $(extpp_headers)
|
23
|
+
MAKEFILE
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
extend(header_files_dependency_injector)
|
28
|
+
|
29
|
+
[
|
30
|
+
File.join(__dir__, "..", "ext", "extpp"),
|
31
|
+
__dir__,
|
32
|
+
].each do |candidate_dir|
|
33
|
+
so_name = "libruby-extpp.#{RbConfig::CONFIG["DLEXT"]}"
|
34
|
+
lib_path = File.expand_path(File.join(candidate_dir, so_name))
|
35
|
+
$LIBS += " #{lib_path.quote}" if File.exist?(lib_path)
|
36
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
|
3
|
+
module Extpp
|
4
|
+
class Compiler
|
5
|
+
attr_reader :cxx_flags
|
6
|
+
def initialize(cxx_flags)
|
7
|
+
@cxx_flags = cxx_flags
|
8
|
+
end
|
9
|
+
|
10
|
+
def gcc?
|
11
|
+
RbConfig::CONFIG["GCC"] == "yes"
|
12
|
+
end
|
13
|
+
|
14
|
+
def check
|
15
|
+
check_debug_build
|
16
|
+
check_version
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def check_debug_build
|
21
|
+
checking_for(checking_message("--enable-debug-build option")) do
|
22
|
+
enable_debug_build = enable_config("debug-build", false)
|
23
|
+
if enable_debug_build
|
24
|
+
@cxx_flags = disable_optimization_build_flag(@cxx_flags)
|
25
|
+
@cxx_flags = enable_debug_build_flag(@cxx_flags)
|
26
|
+
end
|
27
|
+
enable_debug_build
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_version
|
32
|
+
return unless gcc?
|
33
|
+
checking_for(checking_message("g++ version"), "%g%s") do
|
34
|
+
version = nil
|
35
|
+
std = nil
|
36
|
+
if /\Ag\+\+ .+ (\d\.\d)\.\d/ =~ `#{RbConfig.expand("$(CXX) --version")}`
|
37
|
+
version = Float($1)
|
38
|
+
if version < 5.1
|
39
|
+
std = "gnu++11"
|
40
|
+
elsif version < 6.1
|
41
|
+
std = "gnu++14"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
if std
|
45
|
+
@cxx_flags += " -std=#{std}"
|
46
|
+
[version, " (#{std})"]
|
47
|
+
else
|
48
|
+
[version, ""]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def disable_optimization_build_flag(flags)
|
54
|
+
if gcc?
|
55
|
+
flags.gsub(/(^|\s)-O\d(\s|$)/, '\\1-O0\\2')
|
56
|
+
else
|
57
|
+
flags
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def enable_debug_build_flag(flags)
|
62
|
+
if gcc?
|
63
|
+
flags.gsub(/(^|\s)(?:-g|-g\d|-ggdb\d?)(\s|$)/, '\\1-g3\\2')
|
64
|
+
else
|
65
|
+
flags
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#include <ruby.hpp>
|
2
|
+
|
3
|
+
extern "C" void
|
4
|
+
Init_cast(void)
|
5
|
+
{
|
6
|
+
rb::Class("Caster").
|
7
|
+
define_method("cast_int32", [](VALUE self, int argc, VALUE *argv) -> VALUE {
|
8
|
+
VALUE rb_n;
|
9
|
+
rb_scan_args(argc, argv, "1", &rb_n);
|
10
|
+
return rb::cast<rb::Object>(rb::cast<int32_t>(rb::Object(rb_n)));
|
11
|
+
}).
|
12
|
+
define_method("cast_int64", [](VALUE self, int argc, VALUE *argv) -> VALUE {
|
13
|
+
VALUE rb_n;
|
14
|
+
rb_scan_args(argc, argv, "1", &rb_n);
|
15
|
+
return rb::cast<rb::Object>(rb::cast<int64_t>(rb::Object(rb_n)));
|
16
|
+
}).
|
17
|
+
define_method("cast_uint32", [](VALUE self, int argc, VALUE *argv) -> VALUE {
|
18
|
+
VALUE rb_n;
|
19
|
+
rb_scan_args(argc, argv, "1", &rb_n);
|
20
|
+
return rb::cast<rb::Object>(rb::cast<uint32_t>(rb::Object(rb_n)));
|
21
|
+
}).
|
22
|
+
define_method("cast_uint64", [](VALUE self, int argc, VALUE *argv) -> VALUE {
|
23
|
+
VALUE rb_n;
|
24
|
+
rb_scan_args(argc, argv, "1", &rb_n);
|
25
|
+
return rb::cast<rb::Object>(rb::cast<uint64_t>(rb::Object(rb_n)));
|
26
|
+
}).
|
27
|
+
define_method("cast_string", [](VALUE self, int argc, VALUE *argv) -> VALUE {
|
28
|
+
VALUE rb_string;
|
29
|
+
rb_scan_args(argc, argv, "1", &rb_string);
|
30
|
+
return rb::cast<rb::Object>(rb::cast<const char *>(rb::Object(rb_string)));
|
31
|
+
});
|
32
|
+
}
|