extpp 0.0.1
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/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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a6f11af116da3bf20bffbb10008e43f8f03e41f
|
4
|
+
data.tar.gz: adf0040c8f4c3e15aa60093b96a774530f9a09d2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '0927493217502f10fc9f96a54ff1970a992ad4eec539c2ce8f5c03a35c22f917f20183c76ee3557ac245c9e033afc5289976a275267299abcb24983048473e94'
|
7
|
+
data.tar.gz: c1b5f0e6710d8abcc2a97a5912176b9536fcd7652870d636b443949f408bbf14dd58711feb174630a84337379868190a6f820efd0e37c2bec26ed481dce2e392
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) 2017 Kouhei Sutou. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions
|
5
|
+
are met:
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
10
|
+
documentation and/or other materials provided with the distribution.
|
11
|
+
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
22
|
+
SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# README
|
2
|
+
|
3
|
+
## Name
|
4
|
+
|
5
|
+
Ext++
|
6
|
+
|
7
|
+
## Description
|
8
|
+
|
9
|
+
Ext++ is a Ruby extension that provides C++ API for writing Ruby extension.
|
10
|
+
|
11
|
+
You can write your Ruby extension easier than Ruby's C API. Because
|
12
|
+
Ext++'s C++ API reduces duplicated code needed for Ruby's C API.
|
13
|
+
|
14
|
+
You can use all Ruby's C API without any adapter layer. Because you
|
15
|
+
can use C API directory from C++.
|
16
|
+
|
17
|
+
## Install
|
18
|
+
|
19
|
+
TODO
|
20
|
+
|
21
|
+
## How to use
|
22
|
+
|
23
|
+
TODO
|
24
|
+
|
25
|
+
## License
|
26
|
+
|
27
|
+
Copyright (C) 2017 Kouhei Sutou
|
28
|
+
|
29
|
+
The 2-Clause BSD License. See [LICENSE.txt](LICENSE.txt) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "bundler/gem_helper"
|
5
|
+
require "packnga"
|
6
|
+
|
7
|
+
base_dir = File.join(File.dirname(__FILE__))
|
8
|
+
|
9
|
+
helper = Bundler::GemHelper.new(base_dir)
|
10
|
+
def helper.version_tag
|
11
|
+
version
|
12
|
+
end
|
13
|
+
|
14
|
+
helper.install
|
15
|
+
spec = helper.gemspec
|
16
|
+
|
17
|
+
Packnga::DocumentTask.new(spec) do |task|
|
18
|
+
task.original_language = "en"
|
19
|
+
task.translate_language = "ja"
|
20
|
+
end
|
21
|
+
|
22
|
+
Packnga::ReleaseTask.new(spec) do
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_extconf(*arguments)
|
26
|
+
cd("ext/extpp") do
|
27
|
+
ruby("extconf.rb", *arguments)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Configure"
|
32
|
+
task :configure do
|
33
|
+
run_extconf
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace :configure do
|
37
|
+
desc "Configure for debug"
|
38
|
+
task :debug do
|
39
|
+
run_extconf("--enable-debug-build")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Run tests"
|
44
|
+
task :test do
|
45
|
+
ruby("test/run-test.rb")
|
46
|
+
end
|
47
|
+
|
48
|
+
task default: ["configure:debug", :test]
|
data/doc/text/news.md
ADDED
data/ext/extpp/class.cpp
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
#include "function.hpp"
|
2
|
+
|
3
|
+
#include <unordered_map>
|
4
|
+
#include <vector>
|
5
|
+
|
6
|
+
namespace rb {
|
7
|
+
using MethodTable = std::unordered_map<ID, Function *>;
|
8
|
+
|
9
|
+
struct MethodDefinition {
|
10
|
+
MethodDefinition(std::string name_, Function *function_) :
|
11
|
+
name(name_),
|
12
|
+
function(function_) {
|
13
|
+
}
|
14
|
+
|
15
|
+
std::string name;
|
16
|
+
Function *function;
|
17
|
+
};
|
18
|
+
using MethodDefinitions = std::vector<MethodDefinition>;
|
19
|
+
}
|
20
|
+
|
21
|
+
namespace {
|
22
|
+
VALUE MethodTable = Qnil;
|
23
|
+
|
24
|
+
void free_method_table(void *data) {
|
25
|
+
auto method_table = static_cast<rb::MethodTable *>(data);
|
26
|
+
delete method_table;
|
27
|
+
}
|
28
|
+
|
29
|
+
const rb_data_type_t MethodTableType = {
|
30
|
+
"MethodTable",
|
31
|
+
{nullptr, free_method_table, nullptr,},
|
32
|
+
nullptr,
|
33
|
+
nullptr,
|
34
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
35
|
+
};
|
36
|
+
|
37
|
+
rb::MethodTable *method_table_from_ruby(VALUE rb_method_table) {
|
38
|
+
rb::MethodTable *method_table;
|
39
|
+
TypedData_Get_Struct(rb_method_table,
|
40
|
+
rb::MethodTable,
|
41
|
+
&MethodTableType,
|
42
|
+
method_table);
|
43
|
+
return method_table;
|
44
|
+
}
|
45
|
+
|
46
|
+
VALUE method_table_to_ruby(rb::MethodTable *method_table) {
|
47
|
+
if (NIL_P(MethodTable)) {
|
48
|
+
MethodTable = rb_define_class("MethodTable", rb_cData);
|
49
|
+
}
|
50
|
+
return TypedData_Wrap_Struct(MethodTable, &MethodTableType, method_table);
|
51
|
+
}
|
52
|
+
|
53
|
+
VALUE MethodDefinitions = Qnil;
|
54
|
+
|
55
|
+
void free_method_definitions(void *data) {
|
56
|
+
auto method_definitions = static_cast<rb::MethodDefinitions *>(data);
|
57
|
+
delete method_definitions;
|
58
|
+
}
|
59
|
+
|
60
|
+
const rb_data_type_t MethodDefinitionsType = {
|
61
|
+
"MethodDefinitions",
|
62
|
+
{nullptr, free_method_definitions, nullptr,},
|
63
|
+
nullptr,
|
64
|
+
nullptr,
|
65
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
66
|
+
};
|
67
|
+
|
68
|
+
rb::MethodDefinitions *method_definitions_from_ruby(VALUE rb_definitions) {
|
69
|
+
rb::MethodDefinitions *definitions;
|
70
|
+
TypedData_Get_Struct(rb_definitions,
|
71
|
+
rb::MethodDefinitions,
|
72
|
+
&MethodDefinitionsType,
|
73
|
+
definitions);
|
74
|
+
return definitions;
|
75
|
+
}
|
76
|
+
|
77
|
+
VALUE method_definitions_to_ruby(rb::MethodDefinitions *definitions) {
|
78
|
+
if (NIL_P(MethodDefinitions)) {
|
79
|
+
MethodDefinitions = rb_define_class("MethodDefinitions", rb_cData);
|
80
|
+
}
|
81
|
+
return TypedData_Wrap_Struct(MethodDefinitions,
|
82
|
+
&MethodDefinitionsType,
|
83
|
+
definitions);
|
84
|
+
}
|
85
|
+
|
86
|
+
VALUE call_func(int argc, VALUE *argv, VALUE self) {
|
87
|
+
auto rb_method_table =
|
88
|
+
rb_ivar_get(rb_obj_class(self), rb_intern("__method_table__"));
|
89
|
+
auto method_table = method_table_from_ruby(rb_method_table);
|
90
|
+
auto method_name_symbol = rb_funcall(self, rb_intern("__method__"), 0);
|
91
|
+
auto function = (*method_table)[rb_sym2id(method_name_symbol)];
|
92
|
+
return function->call(self, argc, argv);
|
93
|
+
}
|
94
|
+
|
95
|
+
bool flush_method_definitions(VALUE klass) {
|
96
|
+
ID id_method_definitions = rb_intern("__method_definitions__");
|
97
|
+
auto rb_definitions = rb_ivar_get(klass, id_method_definitions);
|
98
|
+
if (NIL_P(rb_definitions)) {
|
99
|
+
return false;
|
100
|
+
}
|
101
|
+
|
102
|
+
auto definitions = method_definitions_from_ruby(rb_definitions);
|
103
|
+
auto rb_method_table = rb_ivar_get(klass, rb_intern("__method_table__"));
|
104
|
+
auto method_table = method_table_from_ruby(rb_method_table);
|
105
|
+
for (const auto &definition : *definitions) {
|
106
|
+
ID name_id = rb_intern(definition.name.c_str());
|
107
|
+
(*method_table)[name_id] = definition.function;
|
108
|
+
rb_define_method(klass,
|
109
|
+
definition.name.c_str(),
|
110
|
+
reinterpret_cast<rb::MethodFunc>(call_func),
|
111
|
+
-1);
|
112
|
+
}
|
113
|
+
rb_ivar_set(klass, id_method_definitions, Qnil);
|
114
|
+
return true;
|
115
|
+
}
|
116
|
+
|
117
|
+
VALUE method_missing(int argc, VALUE *argv, VALUE self) {
|
118
|
+
auto klass = rb_obj_class(self);
|
119
|
+
|
120
|
+
if (flush_method_definitions(klass)) {
|
121
|
+
auto rb_method_table = rb_ivar_get(klass, rb_intern("__method_table__"));
|
122
|
+
auto method_table = method_table_from_ruby(rb_method_table);
|
123
|
+
|
124
|
+
VALUE rb_name_symbol;
|
125
|
+
VALUE rb_args;
|
126
|
+
rb_scan_args(argc, argv, "1*", &rb_name_symbol, &rb_args);
|
127
|
+
auto function = (*method_table)[rb_sym2id(rb_name_symbol)];
|
128
|
+
if (function) {
|
129
|
+
return function->call(self, RARRAY_LEN(rb_args), RARRAY_PTR(rb_args));
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
return rb_call_super(argc, argv);
|
134
|
+
}
|
135
|
+
|
136
|
+
VALUE respond_to_missing_p(VALUE self,
|
137
|
+
VALUE rb_name_symbol,
|
138
|
+
VALUE rb_include_private) {
|
139
|
+
auto klass = rb_obj_class(self);
|
140
|
+
|
141
|
+
if (flush_method_definitions(klass)) {
|
142
|
+
auto rb_method_table = rb_ivar_get(klass, rb_intern("__method_table__"));
|
143
|
+
auto method_table = method_table_from_ruby(rb_method_table);
|
144
|
+
|
145
|
+
auto function = (*method_table)[rb_sym2id(rb_name_symbol)];
|
146
|
+
if (function) {
|
147
|
+
return Qtrue;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
VALUE rb_args[] = {rb_name_symbol, rb_include_private};
|
152
|
+
return rb_call_super(2, rb_args);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
namespace rb {
|
157
|
+
class Class::ClassImpl {
|
158
|
+
public:
|
159
|
+
ClassImpl(VALUE klass) :
|
160
|
+
class_(klass),
|
161
|
+
method_table_(new MethodTable()),
|
162
|
+
lazy_define_method_(false),
|
163
|
+
method_definitions_(nullptr) {
|
164
|
+
rb_iv_set(class_, "__method_table__", method_table_to_ruby(method_table_));
|
165
|
+
rb_iv_set(class_, "__method_definitions__", Qnil);
|
166
|
+
}
|
167
|
+
|
168
|
+
void enable_lazy_define_method() {
|
169
|
+
if (lazy_define_method_) {
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
lazy_define_method_ = true;
|
174
|
+
method_definitions_ = new MethodDefinitions();
|
175
|
+
rb_iv_set(class_, "__method_definitions__",
|
176
|
+
method_definitions_to_ruby(method_definitions_));
|
177
|
+
rb_define_method(class_,
|
178
|
+
"method_missing",
|
179
|
+
reinterpret_cast<MethodFunc>(method_missing),
|
180
|
+
-1);
|
181
|
+
rb_define_method(class_,
|
182
|
+
"respond_to_missing?",
|
183
|
+
reinterpret_cast<MethodFunc>(respond_to_missing_p),
|
184
|
+
-1);
|
185
|
+
}
|
186
|
+
|
187
|
+
void define_method(const char *name, VALUE (*body)(VALUE self)) {
|
188
|
+
rb_define_method(class_,
|
189
|
+
name,
|
190
|
+
reinterpret_cast<MethodFunc>(body),
|
191
|
+
0);
|
192
|
+
}
|
193
|
+
|
194
|
+
void define_method(const char *name,
|
195
|
+
VALUE (*body)(int argc, VALUE *argv, VALUE self)) {
|
196
|
+
rb_define_method(class_,
|
197
|
+
name,
|
198
|
+
reinterpret_cast<MethodFunc>(body),
|
199
|
+
-1);
|
200
|
+
}
|
201
|
+
|
202
|
+
void define_method(const char *name, Function *function) {
|
203
|
+
if (lazy_define_method_) {
|
204
|
+
method_definitions_->emplace_back(name, function);
|
205
|
+
} else {
|
206
|
+
ID name_id = rb_intern(name);
|
207
|
+
(*method_table_)[name_id] = function;
|
208
|
+
rb_define_method(class_,
|
209
|
+
name,
|
210
|
+
reinterpret_cast<MethodFunc>(call_func),
|
211
|
+
-1);
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
private:
|
216
|
+
VALUE class_;
|
217
|
+
MethodTable *method_table_;
|
218
|
+
bool lazy_define_method_;
|
219
|
+
MethodDefinitions *method_definitions_;
|
220
|
+
};
|
221
|
+
|
222
|
+
Class::Class(const char *name, VALUE parent) :
|
223
|
+
Object(rb_define_class(name, parent)),
|
224
|
+
impl_(new ClassImpl(this->to_ruby())) {
|
225
|
+
}
|
226
|
+
|
227
|
+
Class::Class(VALUE klass) :
|
228
|
+
Object(klass),
|
229
|
+
impl_(new ClassImpl(this->to_ruby())) {
|
230
|
+
}
|
231
|
+
|
232
|
+
Class::~Class() {
|
233
|
+
delete impl_;
|
234
|
+
}
|
235
|
+
|
236
|
+
Class &Class::define_method(const char *name,
|
237
|
+
MethodWithoutArguments body) {
|
238
|
+
auto function = new FunctionWithoutArgument(body);
|
239
|
+
impl_->define_method(name, function);
|
240
|
+
return (Class &)*this;
|
241
|
+
}
|
242
|
+
|
243
|
+
Class &Class::define_method(const char *name,
|
244
|
+
MethodWithArguments body) {
|
245
|
+
auto function = new FunctionWithArguments(body);
|
246
|
+
impl_->define_method(name, function);
|
247
|
+
return (Class &)*this;
|
248
|
+
}
|
249
|
+
|
250
|
+
Class &Class::define_method(const char *name,
|
251
|
+
MethodWithArgumentsCompatible body) {
|
252
|
+
auto function = new FunctionWithArgumentsCompatible(body);
|
253
|
+
impl_->define_method(name, function);
|
254
|
+
return (Class &)*this;
|
255
|
+
}
|
256
|
+
|
257
|
+
Class &Class::enable_lazy_define_method() {
|
258
|
+
impl_->enable_lazy_define_method();
|
259
|
+
return (Class &)*this;
|
260
|
+
}
|
261
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative "../../lib/extpp/compiler"
|
2
|
+
|
3
|
+
cxxflags = RbConfig::CONFIG["CXXFLAGS"]
|
4
|
+
compiler = Extpp::Compiler.new(cxxflags)
|
5
|
+
compiler.check
|
6
|
+
cxxflags = compiler.cxx_flags
|
7
|
+
|
8
|
+
sources = Dir.chdir(__dir__) do
|
9
|
+
Dir.glob("*.cpp").collect do |cpp_source|
|
10
|
+
File.join(__dir__, cpp_source)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
objects = sources.collect do |source|
|
14
|
+
source.gsub(/\.cpp\z/, ".o")
|
15
|
+
end
|
16
|
+
|
17
|
+
def collect_headers(dir)
|
18
|
+
Dir.chdir(dir) do
|
19
|
+
Dir.glob("**/*.hpp").collect do |header|
|
20
|
+
File.join(dir, header)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
include_dir = File.expand_path(File.join(__dir__, "..", "..", "include"))
|
26
|
+
public_headers = collect_headers(include_dir)
|
27
|
+
private_headers = collect_headers(__dir__)
|
28
|
+
headers = public_headers + private_headers
|
29
|
+
|
30
|
+
File.open("Makefile", "w") do |makefile|
|
31
|
+
makefile.puts(<<-MAKEFILE)
|
32
|
+
LIBRARY = libruby-extpp.#{RbConfig::CONFIG["DLEXT"]}
|
33
|
+
|
34
|
+
SOURCES = #{sources.collect(&:quote).join(" ")}
|
35
|
+
OBJECTS = #{objects.collect(&:quote).join(" ")}
|
36
|
+
HEADERS = #{headers.collect(&:quote).join(" ")}
|
37
|
+
|
38
|
+
INCLUDE_DIR = #{include_dir.quote}
|
39
|
+
|
40
|
+
CXX = #{RbConfig::CONFIG["CXX"].quote}
|
41
|
+
|
42
|
+
RUBY = #{RbConfig.ruby.quote}
|
43
|
+
RUBY_HEADER_DIR = #{RbConfig::CONFIG["rubyhdrdir"].quote}
|
44
|
+
RUBY_ARCH_HEADER_DIR = #{RbConfig::CONFIG["rubyarchhdrdir"].quote}
|
45
|
+
LDSHAREDXX = #{RbConfig::CONFIG["LDSHAREDXX"]}
|
46
|
+
CCDLFLAGS = #{RbConfig::CONFIG["CCDLFLAGS"]}
|
47
|
+
|
48
|
+
INCLUDEFLAGS = \
|
49
|
+
-I$(INCLUDE_DIR) \
|
50
|
+
-I$(RUBY_HEADER_DIR) \
|
51
|
+
-I$(RUBY_ARCH_HEADER_DIR)
|
52
|
+
CPPFLAGS = #{RbConfig::CONFIG["CPPFLAGS"]}
|
53
|
+
CXXFLAGS = $(CCDLFLAGS) #{cxxflags}
|
54
|
+
|
55
|
+
all: $(LIBRARY)
|
56
|
+
|
57
|
+
clean:
|
58
|
+
rm -rf $(OBJECTS) $(LIBRARY)
|
59
|
+
|
60
|
+
dist-clean:
|
61
|
+
$(MAKE) clean
|
62
|
+
rm -rf Makefile
|
63
|
+
|
64
|
+
install: $(LIBRARY)
|
65
|
+
"$(RUBY)" -run -e install -- $(LIBRARY) $(DESTDIR)/tmp/local/lib/
|
66
|
+
|
67
|
+
$(LIBRARY): $(OBJECTS)
|
68
|
+
$(LDSHAREDXX) -o $@ $^
|
69
|
+
|
70
|
+
.cpp.o:
|
71
|
+
$(CXX) $(INCLUDEFLAGS) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
|
72
|
+
|
73
|
+
$(OBJECTS): $(HEADERS)
|
74
|
+
MAKEFILE
|
75
|
+
end
|