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.
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
+