rbplusplus 0.8 → 0.9
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.
- 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
|
+
|