rbplusplus 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/Rakefile +13 -13
  2. data/TODO +9 -41
  3. data/lib/rbplusplus/builders/allocation_strategy.rb +57 -0
  4. data/lib/rbplusplus/builders/base.rb +115 -212
  5. data/lib/rbplusplus/builders/class.rb +129 -115
  6. data/lib/rbplusplus/builders/const.rb +30 -0
  7. data/lib/rbplusplus/builders/const_converter.rb +52 -0
  8. data/lib/rbplusplus/builders/constructor.rb +19 -0
  9. data/lib/rbplusplus/builders/director.rb +149 -0
  10. data/lib/rbplusplus/builders/director_method.rb +20 -0
  11. data/lib/rbplusplus/builders/enumeration.rb +19 -26
  12. data/lib/rbplusplus/builders/extension.rb +42 -54
  13. data/lib/rbplusplus/builders/global_function.rb +18 -0
  14. data/lib/rbplusplus/builders/helpers/class.rb +74 -0
  15. data/lib/rbplusplus/builders/helpers/enumeration.rb +28 -0
  16. data/lib/rbplusplus/builders/helpers/module.rb +22 -0
  17. data/lib/rbplusplus/builders/include.rb +32 -0
  18. data/lib/rbplusplus/builders/instance_variable.rb +36 -0
  19. data/lib/rbplusplus/builders/method.rb +14 -0
  20. data/lib/rbplusplus/builders/method_base.rb +136 -0
  21. data/lib/rbplusplus/builders/module.rb +37 -51
  22. data/lib/rbplusplus/builders/module_function.rb +16 -0
  23. data/lib/rbplusplus/builders/static_method.rb +14 -0
  24. data/lib/rbplusplus/extension.rb +140 -28
  25. data/lib/rbplusplus/logger.rb +45 -0
  26. data/lib/rbplusplus/module.rb +55 -1
  27. data/lib/rbplusplus/transformers/class.rb +116 -35
  28. data/lib/rbplusplus/transformers/function.rb +14 -16
  29. data/lib/rbplusplus/transformers/method.rb +26 -1
  30. data/lib/rbplusplus/transformers/namespace.rb +12 -0
  31. data/lib/rbplusplus/transformers/node.rb +47 -54
  32. data/lib/rbplusplus/transformers/node_cache.rb +5 -9
  33. data/lib/rbplusplus/writers/multiple_files_writer.rb +290 -88
  34. data/lib/rbplusplus/writers/single_file_writer.rb +36 -14
  35. data/lib/rbplusplus.rb +44 -18
  36. data/test/allocation_strategies_test.rb +33 -0
  37. data/test/class_methods_encapsulate_test.rb +59 -0
  38. data/test/class_methods_test.rb +2 -35
  39. data/test/classes_test.rb +72 -2
  40. data/test/compiling_test.rb +13 -0
  41. data/test/constructors_test.rb +9 -18
  42. data/test/custom_code_test.rb +53 -0
  43. data/test/default_arguments_test.rb +69 -0
  44. data/test/director_test.rb +173 -0
  45. data/test/enumerations_test.rb +29 -0
  46. data/test/extension_test.rb +7 -2
  47. data/test/file_writers_test.rb +11 -4
  48. data/test/function_pointer_test.rb +56 -0
  49. data/test/function_pointers_classes_test.rb +27 -0
  50. data/test/generated/extconf.rb +2 -2
  51. data/test/headers/Adder.cpp +8 -0
  52. data/test/headers/Adder.h +31 -1
  53. data/test/headers/alloc_strats.h +26 -0
  54. data/test/headers/class_methods.h +30 -0
  55. data/test/headers/code/custom_to_from_ruby.cpp +11 -0
  56. data/test/headers/code/custom_to_from_ruby.hpp +13 -0
  57. data/test/headers/constructors.h +8 -20
  58. data/test/headers/default_arguments.h +49 -0
  59. data/test/headers/director.h +148 -0
  60. data/test/headers/enums.h +33 -0
  61. data/test/headers/function_pointers.h +32 -0
  62. data/test/headers/function_pointers_class.h +26 -0
  63. data/test/headers/needs_code.h +10 -0
  64. data/test/headers/overload.h +0 -3
  65. data/test/headers/subclass.h +10 -0
  66. data/test/headers/to_from_ruby.h +6 -4
  67. data/test/headers/ugly_interface.h +4 -7
  68. data/test/modules_test.rb +11 -6
  69. data/test/overloading_test.rb +6 -2
  70. data/test/subclass_test.rb +20 -10
  71. data/test/test_helper.rb +6 -1
  72. data/test/to_from_ruby_test.rb +0 -2
  73. data/test/wrap_as_test.rb +28 -37
  74. metadata +89 -57
  75. data/lib/rbplusplus/builders/types_manager.rb +0 -93
  76. data/lib/rbplusplus/transformers/constructor.rb +0 -4
  77. data/lib/rbplusplus/transformers/module.rb +0 -71
  78. data/lib/rbplusplus/transformers/node_reference.rb +0 -30
  79. data/test/headers/ugly_helper.h +0 -18
  80. data/test/object_persistence_test.rb +0 -44
@@ -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 with this
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, aka in your code
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
- # It is recommended that you use the block format of this class's initializer.
16
- # If you want more fine-grained control of the whole process, don't use
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
- # #sources - The directory / array / name of C++ header files to parse.
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
- # #working_dir - Specify the directory where the code will be generated. This needs
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 the non-block format, you need to manually fire the different steps of the
32
- # code generation process, and in this order:
28
+ # In immediate mode, you must to manually fire the different steps of the
29
+ # code generation process in this order:
33
30
  #
34
- # #build - Fires the code generation process
31
+ # e.build - Fires the code generation process
35
32
  #
36
- # #write - Writes out the generated code into files
33
+ # e.write - Writes out the generated code into files
37
34
  #
38
- # #compile - Compiles the generated code into a Ruby extension.
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 module built into the extension.
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
- if block
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
- # For now, to get access to the underlying RbGCCXML query system, save the
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
- # How should we write out the source code? This can be one of two modes:
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
- @builder = Builders::ExtensionBuilder.new(@name, @node || @parser)
175
- Builders::Base.additional_includes = @options[:includes]
176
- Builders::Base.sources = @sources
177
- @builder.modules = @modules
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 @working_dir. View this
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(eval("__FILE__", block.binding)), "generated"))
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
@@ -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
- @modules << RbModule.new(name, @parser, &block)
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").as_method
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
- @classes ||= []
23
- @classes << RbPlusPlus::NodeReference.new(val)
22
+ cache[:classes] ||= []
23
+ cache[:classes] << val
24
24
  else
25
- @methods ||= []
26
- @methods << RbPlusPlus::NodeReference.new(val)
25
+ cache[:methods] ||= []
26
+ cache[:methods] << val
27
27
  end
28
- val.moved=true
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
- nodes = node_methods(*args)
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
- alias_method :node_classes, :classes
42
- def classes(*args) #:nodoc:
43
- [@classes || [], node_classes].flatten
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
- # returns a list of superclasses of this node, including the node's class
47
- def super_classes
48
- retv = []
49
- unless node.attributes['bases'].nil? || node.attributes['bases'] == ""
50
- node.attributes['bases'].split.each do |cls_id|
51
- c = XMLParsing.find(:type => "Class", :id => cls_id)
52
- c = XMLParsing.find(:type => "Struct", :id => cls_id) if c.nil?
53
- if c.nil?
54
- puts "#{self.qualified_name} cannot find super class for id #{cls_id} "
55
- next
56
- end
57
- c = RbPlusPlus::NodeCache.instance.get(c)
58
- retv << c unless c.ignored?
59
- end
60
- end
61
-
62
- return retv
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
- attr_reader :special_qualified_name
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
- !(@as_method || false)
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
- @as_method = true
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
- alias_method :method_qualified_name, :qualified_name
23
- def qualified_name #:nodoc:
24
- @special_qualified_name || method_qualified_name
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 #:nodoc:
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
+
@@ -0,0 +1,12 @@
1
+ module RbGCCXML
2
+ class Namespace < Node
3
+
4
+ # For easy compatibility between #methods
5
+ # and #functions in the builder system
6
+ def methods(*args) #:nodoc:
7
+ self.functions(*args)
8
+ end
9
+
10
+ end
11
+ end
12
+