rbplusplus 0.8 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +13 -13
- data/TODO +9 -41
- data/lib/rbplusplus/builders/allocation_strategy.rb +57 -0
- data/lib/rbplusplus/builders/base.rb +115 -212
- data/lib/rbplusplus/builders/class.rb +129 -115
- data/lib/rbplusplus/builders/const.rb +30 -0
- data/lib/rbplusplus/builders/const_converter.rb +52 -0
- data/lib/rbplusplus/builders/constructor.rb +19 -0
- data/lib/rbplusplus/builders/director.rb +149 -0
- data/lib/rbplusplus/builders/director_method.rb +20 -0
- data/lib/rbplusplus/builders/enumeration.rb +19 -26
- data/lib/rbplusplus/builders/extension.rb +42 -54
- data/lib/rbplusplus/builders/global_function.rb +18 -0
- data/lib/rbplusplus/builders/helpers/class.rb +74 -0
- data/lib/rbplusplus/builders/helpers/enumeration.rb +28 -0
- data/lib/rbplusplus/builders/helpers/module.rb +22 -0
- data/lib/rbplusplus/builders/include.rb +32 -0
- data/lib/rbplusplus/builders/instance_variable.rb +36 -0
- data/lib/rbplusplus/builders/method.rb +14 -0
- data/lib/rbplusplus/builders/method_base.rb +136 -0
- data/lib/rbplusplus/builders/module.rb +37 -51
- data/lib/rbplusplus/builders/module_function.rb +16 -0
- data/lib/rbplusplus/builders/static_method.rb +14 -0
- data/lib/rbplusplus/extension.rb +140 -28
- data/lib/rbplusplus/logger.rb +45 -0
- data/lib/rbplusplus/module.rb +55 -1
- data/lib/rbplusplus/transformers/class.rb +116 -35
- data/lib/rbplusplus/transformers/function.rb +14 -16
- data/lib/rbplusplus/transformers/method.rb +26 -1
- data/lib/rbplusplus/transformers/namespace.rb +12 -0
- data/lib/rbplusplus/transformers/node.rb +47 -54
- data/lib/rbplusplus/transformers/node_cache.rb +5 -9
- data/lib/rbplusplus/writers/multiple_files_writer.rb +290 -88
- data/lib/rbplusplus/writers/single_file_writer.rb +36 -14
- data/lib/rbplusplus.rb +44 -18
- data/test/allocation_strategies_test.rb +33 -0
- data/test/class_methods_encapsulate_test.rb +59 -0
- data/test/class_methods_test.rb +2 -35
- data/test/classes_test.rb +72 -2
- data/test/compiling_test.rb +13 -0
- data/test/constructors_test.rb +9 -18
- data/test/custom_code_test.rb +53 -0
- data/test/default_arguments_test.rb +69 -0
- data/test/director_test.rb +173 -0
- data/test/enumerations_test.rb +29 -0
- data/test/extension_test.rb +7 -2
- data/test/file_writers_test.rb +11 -4
- data/test/function_pointer_test.rb +56 -0
- data/test/function_pointers_classes_test.rb +27 -0
- data/test/generated/extconf.rb +2 -2
- data/test/headers/Adder.cpp +8 -0
- data/test/headers/Adder.h +31 -1
- data/test/headers/alloc_strats.h +26 -0
- data/test/headers/class_methods.h +30 -0
- data/test/headers/code/custom_to_from_ruby.cpp +11 -0
- data/test/headers/code/custom_to_from_ruby.hpp +13 -0
- data/test/headers/constructors.h +8 -20
- data/test/headers/default_arguments.h +49 -0
- data/test/headers/director.h +148 -0
- data/test/headers/enums.h +33 -0
- data/test/headers/function_pointers.h +32 -0
- data/test/headers/function_pointers_class.h +26 -0
- data/test/headers/needs_code.h +10 -0
- data/test/headers/overload.h +0 -3
- data/test/headers/subclass.h +10 -0
- data/test/headers/to_from_ruby.h +6 -4
- data/test/headers/ugly_interface.h +4 -7
- data/test/modules_test.rb +11 -6
- data/test/overloading_test.rb +6 -2
- data/test/subclass_test.rb +20 -10
- data/test/test_helper.rb +6 -1
- data/test/to_from_ruby_test.rb +0 -2
- data/test/wrap_as_test.rb +28 -37
- metadata +89 -57
- data/lib/rbplusplus/builders/types_manager.rb +0 -93
- data/lib/rbplusplus/transformers/constructor.rb +0 -4
- data/lib/rbplusplus/transformers/module.rb +0 -71
- data/lib/rbplusplus/transformers/node_reference.rb +0 -30
- data/test/headers/ugly_helper.h +0 -18
- data/test/object_persistence_test.rb +0 -44
data/lib/rbplusplus/extension.rb
CHANGED
@@ -1,41 +1,38 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
1
3
|
module RbPlusPlus
|
2
4
|
|
3
|
-
# This is the starting class for Rb++ wrapping. All Rb++ projects start
|
4
|
-
# class:
|
5
|
+
# This is the starting class for Rb++ wrapping. All Rb++ projects start as such:
|
5
6
|
#
|
6
7
|
# Extension.new "extension_name" do |e|
|
7
8
|
# ...
|
8
9
|
# end
|
9
10
|
#
|
10
|
-
# "extension_name" is what the resulting Ruby library will be named
|
11
|
-
# you will have
|
12
|
-
#
|
13
|
-
# require "extension_name"
|
11
|
+
# where "extension_name" is what the resulting Ruby library will be named.
|
14
12
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# the block format. Instead you should do the following:
|
13
|
+
# For most cases, the block format will work. If you need more detailed control
|
14
|
+
# over the code generation process, you can use an immediate mode:
|
18
15
|
#
|
19
16
|
# e = Extension.new "extension_name"
|
20
17
|
# ...
|
21
18
|
#
|
22
19
|
# The following calls are required in both formats:
|
23
20
|
#
|
24
|
-
#
|
21
|
+
# e.sources - The directory / array / name of C++ header files to parse.
|
25
22
|
#
|
26
23
|
# In the non-block format, the following calls are required:
|
27
24
|
#
|
28
|
-
#
|
25
|
+
# e.working_dir - Specify the directory where the code will be generated. This needs
|
29
26
|
# to be a full path.
|
30
27
|
#
|
31
|
-
# In
|
32
|
-
# code generation process
|
28
|
+
# In immediate mode, you must to manually fire the different steps of the
|
29
|
+
# code generation process in this order:
|
33
30
|
#
|
34
|
-
#
|
31
|
+
# e.build - Fires the code generation process
|
35
32
|
#
|
36
|
-
#
|
33
|
+
# e.write - Writes out the generated code into files
|
37
34
|
#
|
38
|
-
#
|
35
|
+
# e.compile - Compiles the generated code into a Ruby extension.
|
39
36
|
#
|
40
37
|
class Extension
|
41
38
|
|
@@ -52,7 +49,9 @@ module RbPlusPlus
|
|
52
49
|
attr_accessor :options
|
53
50
|
|
54
51
|
# Create a new Ruby extension with a given name. This name will be
|
55
|
-
# the
|
52
|
+
# the actual name of the extension, e.g. you'll have name.so and you will
|
53
|
+
# call require 'name' when using your new extension.
|
54
|
+
#
|
56
55
|
# This constructor can be standalone or take a block.
|
57
56
|
def initialize(name, &block)
|
58
57
|
@name = name
|
@@ -71,7 +70,11 @@ module RbPlusPlus
|
|
71
70
|
|
72
71
|
@node = nil
|
73
72
|
|
74
|
-
|
73
|
+
parse_command_line
|
74
|
+
|
75
|
+
if requesting_console?
|
76
|
+
block.call(self) if block
|
77
|
+
elsif block
|
75
78
|
build_working_dir(&block)
|
76
79
|
block.call(self)
|
77
80
|
build
|
@@ -93,9 +96,24 @@ module RbPlusPlus
|
|
93
96
|
# * <tt>:ldflags</tt> - Flag(s) to be added to command line for linking
|
94
97
|
# * <tt>:includes</tt> - Header file(s) to include at the beginning of each .rb.cpp file generated.
|
95
98
|
# * <tt>:include_source_files</tt> - C++ source files that need to be compiled into the extension but not wrapped.
|
99
|
+
# * <tt>:include_source_dir</tt> - A combination option for reducing duplication, this option will
|
100
|
+
# query the given directory for source files, adding all to <tt>:include_source_files</tt> and
|
101
|
+
# adding all h/hpp files to <tt>:includes</tt>
|
102
|
+
#
|
96
103
|
def sources(dirs, options = {})
|
97
104
|
parser_options = {}
|
98
105
|
|
106
|
+
if (code_dir = options.delete(:include_source_dir))
|
107
|
+
options[:include_source_files] ||= []
|
108
|
+
options[:includes] ||= []
|
109
|
+
Dir["#{code_dir}/*"].each do |f|
|
110
|
+
next if File.directory?(f)
|
111
|
+
|
112
|
+
options[:include_source_files] << f
|
113
|
+
options[:includes] << f if File.extname(f) =~ /hpp/i || File.extname(f) =~ /h/i
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
99
117
|
if (paths = options.delete(:include_paths))
|
100
118
|
@options[:include_paths] << paths
|
101
119
|
parser_options[:includes] = paths
|
@@ -131,8 +149,15 @@ module RbPlusPlus
|
|
131
149
|
end
|
132
150
|
end
|
133
151
|
|
152
|
+
@options[:includes] += [*dirs]
|
153
|
+
|
134
154
|
@sources = Dir.glob dirs
|
155
|
+
Logger.info "Parsing #{@sources.inspect}"
|
135
156
|
@parser = RbGCCXML.parse(dirs, parser_options)
|
157
|
+
|
158
|
+
if requesting_console?
|
159
|
+
start_console
|
160
|
+
end
|
136
161
|
end
|
137
162
|
|
138
163
|
# Set a namespace to be the main namespace used for this extension.
|
@@ -140,7 +165,7 @@ module RbPlusPlus
|
|
140
165
|
# class, enums, etc to be globally available to Ruby (aka not in it's own
|
141
166
|
# module)
|
142
167
|
#
|
143
|
-
#
|
168
|
+
# To get access to the underlying RbGCCXML query system, save the
|
144
169
|
# return value of this method:
|
145
170
|
#
|
146
171
|
# node = namespace "lib::to_wrap"
|
@@ -158,9 +183,11 @@ module RbPlusPlus
|
|
158
183
|
m
|
159
184
|
end
|
160
185
|
|
161
|
-
#
|
186
|
+
# Specify the mode with which to write out code files. This can be one of two modes:
|
187
|
+
#
|
162
188
|
# * <tt>:multiple</tt> (default) - Each class and module gets it's own set of hpp/cpp files
|
163
189
|
# * <tt>:single</tt> - Everything gets written to a single file
|
190
|
+
#
|
164
191
|
def writer_mode(mode)
|
165
192
|
raise "Unknown writer mode #{mode}" unless [:multiple, :single].include?(mode)
|
166
193
|
@writer_mode = mode
|
@@ -171,16 +198,20 @@ module RbPlusPlus
|
|
171
198
|
raise ConfigurationError.new("Must specify working directory") unless @working_dir
|
172
199
|
raise ConfigurationError.new("Must specify which sources to wrap") unless @parser
|
173
200
|
|
174
|
-
|
175
|
-
|
176
|
-
Builders::
|
177
|
-
@builder.
|
201
|
+
Logger.info "Beginning code generation"
|
202
|
+
|
203
|
+
@builder = Builders::ExtensionNode.new(@name, @node || @parser, @modules)
|
204
|
+
@builder.add_includes @options[:includes]
|
178
205
|
@builder.build
|
206
|
+
@builder.sort
|
207
|
+
|
208
|
+
Logger.info "Code generation complete"
|
179
209
|
end
|
180
210
|
|
181
211
|
# Write out the generated code into files.
|
182
212
|
# #build must be called before this step or nothing will be written out
|
183
213
|
def write
|
214
|
+
Logger.info "Writing code to files"
|
184
215
|
prepare_working_dir
|
185
216
|
process_other_source_files
|
186
217
|
|
@@ -192,26 +223,74 @@ module RbPlusPlus
|
|
192
223
|
extconf = Writers::ExtensionWriter.new(@builder, @working_dir)
|
193
224
|
extconf.options = @options
|
194
225
|
extconf.write
|
226
|
+
Logger.info "Files written"
|
195
227
|
end
|
196
228
|
|
197
229
|
# Compile the extension.
|
198
|
-
# This will create an rbpp_compile.log file in
|
230
|
+
# This will create an rbpp_compile.log file in +working_dir+. View this
|
199
231
|
# file to see the full compilation process including any compiler
|
200
232
|
# errors / warnings.
|
201
233
|
def compile
|
234
|
+
Logger.info "Compiling. See rbpp_compile.log for details."
|
235
|
+
require 'rbconfig'
|
236
|
+
ruby = File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])
|
202
237
|
FileUtils.cd @working_dir do
|
203
|
-
system("ruby extconf.rb > rbpp_compile.log 2>&1")
|
238
|
+
system("#{ruby} extconf.rb > rbpp_compile.log 2>&1")
|
239
|
+
system("rm -f *.so")
|
204
240
|
system("make >> rbpp_compile.log 2>&1")
|
205
241
|
end
|
242
|
+
Logger.info "Compilation complete."
|
206
243
|
end
|
207
244
|
|
208
245
|
protected
|
209
246
|
|
247
|
+
# Read any command line arguments and process them
|
248
|
+
def parse_command_line
|
249
|
+
OptionParser.new do |opts|
|
250
|
+
opts.banner = "Usage: ruby #{$0} [options]"
|
251
|
+
|
252
|
+
opts.on_head("-h", "--help", "Show this help message") do
|
253
|
+
puts opts
|
254
|
+
exit
|
255
|
+
end
|
256
|
+
|
257
|
+
opts.on("-v", "--verbose", "Show all progress messages (INFO, DEBUG, WARNING, ERROR)") do
|
258
|
+
Logger.verbose = true
|
259
|
+
end
|
260
|
+
|
261
|
+
opts.on("-q", "--quiet", "Only show WARNING and ERROR messages") do
|
262
|
+
Logger.quiet = true
|
263
|
+
end
|
264
|
+
|
265
|
+
opts.on("--console", "Open up a console to query the source via rbgccxml") do
|
266
|
+
@requesting_console = true
|
267
|
+
end
|
268
|
+
|
269
|
+
opts.on("--clean", "Force a complete clean and rebuild of this extension") do
|
270
|
+
@force_rebuild = true
|
271
|
+
end
|
272
|
+
|
273
|
+
end.parse!
|
274
|
+
end
|
275
|
+
|
276
|
+
# Check ARGV to see if someone asked for "console"
|
277
|
+
def requesting_console?
|
278
|
+
@requesting_console
|
279
|
+
end
|
280
|
+
|
281
|
+
# Start up a new IRB console session giving the user access
|
282
|
+
# to the RbGCCXML parser instance to do real-time querying
|
283
|
+
# of the code they're trying to wrap
|
284
|
+
def start_console
|
285
|
+
puts "IRB Session starting. @parser is now available to you for querying your code. The extension object is available as 'self'"
|
286
|
+
IRB.start_session(binding)
|
287
|
+
end
|
288
|
+
|
210
289
|
# If the working dir doesn't exist, make it
|
211
290
|
# and if it does exist, clean it out
|
212
291
|
def prepare_working_dir
|
213
292
|
FileUtils.mkdir_p @working_dir unless File.directory?(@working_dir)
|
214
|
-
FileUtils.rm_rf Dir["#{@working_dir}/*"]
|
293
|
+
FileUtils.rm_rf Dir["#{@working_dir}/*"] if @force_rebuild #ARGV.include?("clean")
|
215
294
|
end
|
216
295
|
|
217
296
|
# Make sure that any files or globs of files in :include_source_files are copied into the working
|
@@ -225,8 +304,41 @@ module RbPlusPlus
|
|
225
304
|
|
226
305
|
# Cool little eval / binding hack, from need.rb
|
227
306
|
def build_working_dir(&block)
|
307
|
+
file_name =
|
308
|
+
if block.respond_to?(:source_location)
|
309
|
+
block.source_location[0]
|
310
|
+
else
|
311
|
+
eval("__FILE__", block.binding)
|
312
|
+
end
|
313
|
+
|
228
314
|
@working_dir = File.expand_path(
|
229
|
-
File.join(File.dirname(
|
315
|
+
File.join(File.dirname(file_name), "generated"))
|
230
316
|
end
|
231
317
|
end
|
232
318
|
end
|
319
|
+
|
320
|
+
require 'irb'
|
321
|
+
|
322
|
+
module IRB # :nodoc:
|
323
|
+
def self.start_session(binding)
|
324
|
+
unless @__initialized
|
325
|
+
args = ARGV
|
326
|
+
ARGV.replace(ARGV.dup)
|
327
|
+
IRB.setup(nil)
|
328
|
+
ARGV.replace(args)
|
329
|
+
@__initialized = true
|
330
|
+
end
|
331
|
+
|
332
|
+
workspace = WorkSpace.new(binding)
|
333
|
+
|
334
|
+
irb = Irb.new(workspace)
|
335
|
+
|
336
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
337
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
338
|
+
|
339
|
+
catch(:IRB_EXIT) do
|
340
|
+
irb.eval_input
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RbPlusPlus
|
2
|
+
# Helper method for getting access to the logger system
|
3
|
+
# Special logger that simply prints out to stdout and stderr
|
4
|
+
# Can be configured to ignore certain warning messages.
|
5
|
+
class Logger
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Tell the logger to print out every message it gets
|
9
|
+
def verbose=(val)
|
10
|
+
@@verbose = val
|
11
|
+
end
|
12
|
+
|
13
|
+
# Tell the logger to be a little quieter
|
14
|
+
def quiet=(val)
|
15
|
+
@@quiet = val
|
16
|
+
end
|
17
|
+
|
18
|
+
def verbose?
|
19
|
+
@@verbose = false unless defined?(@@verbose)
|
20
|
+
@@verbose
|
21
|
+
end
|
22
|
+
|
23
|
+
def quiet?
|
24
|
+
@@quiet = false unless defined?(@@quiet)
|
25
|
+
@@quiet
|
26
|
+
end
|
27
|
+
|
28
|
+
def info(msg)
|
29
|
+
$stdout.puts "(INFO) #{msg}" unless quiet?
|
30
|
+
end
|
31
|
+
|
32
|
+
def warn(type, msg)
|
33
|
+
$stdout.puts "(WARNING) #{msg}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug(msg)
|
37
|
+
$stdout.puts "(DEBUG) #{msg}" if verbose?
|
38
|
+
end
|
39
|
+
|
40
|
+
def error(msg)
|
41
|
+
$stderr.puts "(ERROR) #{msg}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/rbplusplus/module.rb
CHANGED
@@ -13,6 +13,9 @@ module RbPlusPlus
|
|
13
13
|
# Access to the underlying RbGCCXML parser
|
14
14
|
attr_reader :node
|
15
15
|
|
16
|
+
# Parent module if this is nested
|
17
|
+
attr_accessor :parent
|
18
|
+
|
16
19
|
# Registers a new module definition for this extension.
|
17
20
|
# Use Extension#module or RbModule#module instead
|
18
21
|
# of creating an instance of this class directly
|
@@ -36,6 +39,9 @@ module RbPlusPlus
|
|
36
39
|
@name = name
|
37
40
|
@parser = parser
|
38
41
|
@modules = []
|
42
|
+
@wrapped_functions = []
|
43
|
+
@wrapped_classes = []
|
44
|
+
@wrapped_structs = []
|
39
45
|
|
40
46
|
block.call(self) if block
|
41
47
|
end
|
@@ -51,7 +57,55 @@ module RbPlusPlus
|
|
51
57
|
# Register another module to be defined inside of
|
52
58
|
# this module. Acts the same as Extension#module.
|
53
59
|
def module(name, &block)
|
54
|
-
|
60
|
+
m = RbModule.new(name, @parser, &block)
|
61
|
+
m.parent = self
|
62
|
+
@modules << m
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add an RbGCCXML::Node to this module. This Node can be a
|
66
|
+
# Function, Class or Struct and will get wrapped accordingly
|
67
|
+
def includes(node)
|
68
|
+
if node.is_a?(RbGCCXML::Function)
|
69
|
+
@wrapped_functions << node
|
70
|
+
elsif node.is_a?(RbGCCXML::Class)
|
71
|
+
@wrapped_classes << node
|
72
|
+
elsif node.is_a?(RbGCCXML::Struct)
|
73
|
+
@wrapped_structs << node
|
74
|
+
else
|
75
|
+
raise "Cannot use #{self.class}#includes for type '#{node.class}'"
|
76
|
+
end
|
77
|
+
|
78
|
+
node.moved_to = self
|
79
|
+
end
|
80
|
+
|
81
|
+
# Make sure to add to the node.functions any functions specifically
|
82
|
+
# given to this module
|
83
|
+
def functions(*args)
|
84
|
+
[node ? node.functions(*args) : [], @wrapped_functions].flatten
|
85
|
+
end
|
86
|
+
|
87
|
+
# As with #functions, add to node.classes any classes / structs that
|
88
|
+
# have been explicitly given to this module.
|
89
|
+
def classes(*args)
|
90
|
+
[node ? node.classes(*args) : [], @wrapped_classes].flatten
|
91
|
+
end
|
92
|
+
|
93
|
+
# See #clases and #functions
|
94
|
+
def structs(*args)
|
95
|
+
[node ? node.structs(*args) : [], @wrapped_structs].flatten
|
96
|
+
end
|
97
|
+
|
98
|
+
def enumerations(*args)
|
99
|
+
node ? node.enumerations(*args) : []
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get the fully nested name of this module
|
103
|
+
def qualified_name
|
104
|
+
if parent
|
105
|
+
"#{parent.qualified_name}::#{self.name}"
|
106
|
+
else
|
107
|
+
self.name
|
108
|
+
end
|
55
109
|
end
|
56
110
|
|
57
111
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module RbGCCXML
|
2
|
-
class Class
|
2
|
+
class Class < Node
|
3
3
|
# Class can include nested classes and nested structs.
|
4
4
|
#
|
5
5
|
# Class can also include external methods/functions as class level methods
|
@@ -11,7 +11,7 @@ module RbGCCXML
|
|
11
11
|
#
|
12
12
|
# or for a instance method:
|
13
13
|
#
|
14
|
-
# math_class.includes node.namespaces("Math").functions("mod").
|
14
|
+
# math_class.includes node.namespaces("Math").functions("mod").as_instance_method
|
15
15
|
#
|
16
16
|
# or for nesting a class/struct:
|
17
17
|
#
|
@@ -19,49 +19,130 @@ module RbGCCXML
|
|
19
19
|
#
|
20
20
|
def includes(val)
|
21
21
|
if (val.is_a?(RbGCCXML::Struct) || val.is_a?(RbGCCXML::Class))
|
22
|
-
|
23
|
-
|
22
|
+
cache[:classes] ||= []
|
23
|
+
cache[:classes] << val
|
24
24
|
else
|
25
|
-
|
26
|
-
|
25
|
+
cache[:methods] ||= []
|
26
|
+
cache[:methods] << val
|
27
27
|
end
|
28
|
-
val.
|
28
|
+
val.moved_to = self
|
29
29
|
end
|
30
30
|
|
31
|
+
alias_method :node_classes, :classes
|
32
|
+
def classes(*args) #:nodoc:
|
33
|
+
find_with_cache(:classes, node_classes(*args))
|
34
|
+
end
|
35
|
+
|
31
36
|
alias_method :node_methods, :methods
|
32
37
|
def methods(*args) #:nodoc:
|
33
|
-
|
34
|
-
methods = @methods || QueryResult.new
|
35
|
-
methods << cache(nodes)
|
36
|
-
methods.flatten!
|
37
|
-
return methods if args.empty?
|
38
|
-
return (methods.size == 1 ? methods[0] : methods)
|
38
|
+
find_with_cache(:methods, node_methods(*args))
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
# Specify which superclass to use.
|
42
|
+
# Because Rice doesn't support multiple inheritance right now,
|
43
|
+
# we need to know which superclass Rice should use for this class.
|
44
|
+
# An error message will show on classes with mutiple superclasses
|
45
|
+
# where this method hasn't been used yet.
|
46
|
+
#
|
47
|
+
# klass should be the node for the class you want to wrap
|
48
|
+
def use_superclass(node)
|
49
|
+
cache[:use_superclass] = node
|
44
50
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
51
|
+
|
52
|
+
def _get_superclass #:nodoc:
|
53
|
+
cache[:use_superclass]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Like #use_superclass, this method allows the user to specify
|
57
|
+
# which constructor Rice should expose to Ruby.
|
58
|
+
# Rice currently, because of the lack of method overloading,
|
59
|
+
# only supports one constructor definition. Having multiple
|
60
|
+
# in the code will work, but only the last defined will actually
|
61
|
+
# work.
|
62
|
+
def use_constructor(node)
|
63
|
+
cache[:use_constructor] = node
|
64
|
+
end
|
65
|
+
|
66
|
+
def _get_constructor #:nodoc:
|
67
|
+
cache[:use_constructor]
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sometimes, type manipulation, moving nodes around, or flat
|
71
|
+
# ignoring nodes just doesn't do the trick and you need to write
|
72
|
+
# your own custom wrapper code. This method is for that. There are
|
73
|
+
# two parts to custom code: the declaration and the wrapping.
|
74
|
+
#
|
75
|
+
# The Declaration:
|
76
|
+
# This is the actual custom code you write. It may need to take
|
77
|
+
# a pointer to the class type as the first parameter
|
78
|
+
# and follow with that any parameters you want.
|
79
|
+
#
|
80
|
+
# The Wrapping
|
81
|
+
# The wrapping is the custom (usually one-line) bit of Rice code that
|
82
|
+
# hooks up your declaration with the class in question. To ensure that
|
83
|
+
# you doesn't need to know the variable of the ruby class object,
|
84
|
+
# use <class> and rb++ will replace it as needed.
|
85
|
+
#
|
86
|
+
# Example (taken from Ogre.rb's wrapping of Ogre)
|
87
|
+
#
|
88
|
+
# decl = <<-END
|
89
|
+
# int RenderTarget_getCustomAttributeInt(Ogre::RenderTarget* self, const std::string& name) {
|
90
|
+
# int value(0);
|
91
|
+
# self->getCustomAttribute(name, &value);
|
92
|
+
# return value;
|
93
|
+
# }
|
94
|
+
# END
|
95
|
+
# wrapping = "<class>.define_method(\"get_custom_attribute_int\", &RenderTarget_getCustomAttributeInt);"
|
96
|
+
#
|
97
|
+
# ogre.classes("RenderTarget").add_custom_code(decl, wrapping)
|
98
|
+
#
|
99
|
+
# This method works as an aggregator, so feel free to use it any number
|
100
|
+
# of times for a class, it won't clobber any previous uses.
|
101
|
+
#
|
102
|
+
def add_custom_code(declaration, wrapping)
|
103
|
+
cache[:declarations] ||= []
|
104
|
+
cache[:declarations] << declaration
|
105
|
+
|
106
|
+
cache[:wrappings] ||= []
|
107
|
+
cache[:wrappings] << wrapping
|
108
|
+
end
|
109
|
+
|
110
|
+
def _get_custom_declarations #:nodoc:
|
111
|
+
cache[:declarations] || []
|
112
|
+
end
|
113
|
+
|
114
|
+
def _get_custom_wrappings #:nodoc:
|
115
|
+
cache[:wrappings] || []
|
116
|
+
end
|
117
|
+
|
118
|
+
# Does this class have virtual methods (especially pure virtual?)
|
119
|
+
# If so, then rb++ will generate a proxy class to handle
|
120
|
+
# the message routing as needed.
|
121
|
+
def needs_director? #:nodoc:
|
122
|
+
!!cache[:build_director] #[methods].flatten.select {|m| m.virtual? }.length > 0
|
123
|
+
end
|
124
|
+
|
125
|
+
# Until all the kinks of the director code generation can be
|
126
|
+
# worked out, rb++ must be told which classes to build
|
127
|
+
# directors for. Simply call this method on the class to do so
|
128
|
+
def director
|
129
|
+
cache[:build_director] = true
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Take the cache key, and the normal results, adds to the results
|
135
|
+
# those that are in the cache and returns them properly.
|
136
|
+
def find_with_cache(type, results)
|
137
|
+
in_cache = cache[type]
|
138
|
+
|
139
|
+
ret = QueryResult.new
|
140
|
+
ret << results if results
|
141
|
+
ret << in_cache if in_cache
|
142
|
+
ret.flatten!
|
143
|
+
|
144
|
+
ret.size == 1 ? ret[0] : ret
|
63
145
|
end
|
64
|
-
|
65
146
|
end
|
66
147
|
end
|
67
148
|
|
@@ -1,27 +1,25 @@
|
|
1
1
|
module RbGCCXML
|
2
|
-
class Function
|
3
|
-
|
4
|
-
|
5
|
-
# always true for functions, false for methods
|
2
|
+
class Function < Node
|
3
|
+
# Always true for functions, false for methods
|
6
4
|
def static?
|
7
|
-
!
|
5
|
+
!cache[:as_method]
|
8
6
|
end
|
9
|
-
|
7
|
+
|
10
8
|
# Sets this function to be an instance method.
|
11
9
|
# Useful for custom function declaration.
|
12
10
|
def as_instance_method
|
13
|
-
|
14
|
-
return self
|
15
|
-
end
|
16
|
-
|
17
|
-
def calls(method_name)
|
18
|
-
@special_qualified_name = method_name
|
11
|
+
cache[:as_method] = true
|
19
12
|
self
|
20
13
|
end
|
21
|
-
|
22
|
-
|
23
|
-
def
|
24
|
-
|
14
|
+
|
15
|
+
# Are we wrapping this function as an instance method?
|
16
|
+
def as_instance_method?
|
17
|
+
!!cache[:as_method]
|
18
|
+
end
|
19
|
+
|
20
|
+
# For Class#needs_director?
|
21
|
+
def purely_virtual?
|
22
|
+
false
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
@@ -1,4 +1,29 @@
|
|
1
1
|
module RbGCCXML
|
2
|
-
class Method
|
2
|
+
class Method < Function
|
3
|
+
|
4
|
+
# Specifies a default return value for the
|
5
|
+
# virtual method wrapper that rb++ will build
|
6
|
+
# for this method.
|
7
|
+
#
|
8
|
+
# This will be needed in the situation where you
|
9
|
+
# have a Ruby wrapped class where a C++ method calls
|
10
|
+
# another method on the same object that's polymorphic.
|
11
|
+
# Rice is unable to figure out the correct path to take,
|
12
|
+
# and usually ends up trying to go back up the chain,
|
13
|
+
# throwing the NotImplementedError.
|
14
|
+
#
|
15
|
+
# Specifying this option will turn the throw line into
|
16
|
+
# a return line.
|
17
|
+
#
|
18
|
+
# See director_test's use of do_process_impl for an
|
19
|
+
# example of this functionality.
|
20
|
+
def default_return_value(value = nil)
|
21
|
+
if value
|
22
|
+
cache[:default_return_value] = value
|
23
|
+
else
|
24
|
+
cache[:default_return_value]
|
25
|
+
end
|
26
|
+
end
|
3
27
|
end
|
4
28
|
end
|
29
|
+
|