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
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rake/rdoctask'
3
3
  require 'rake/contrib/sshpublisher'
4
4
 
5
5
  PROJECT_NAME = "rb++"
6
- RBPLUSPLUS_VERSION = "0.8"
6
+ RBPLUSPLUS_VERSION = "0.9"
7
7
 
8
8
  task :default => :test
9
9
 
@@ -16,10 +16,13 @@ task :default => :test
16
16
  # tests individually
17
17
  desc "Run the tests"
18
18
  task :test do
19
- files = FileList["test/*_test.rb"]
20
- puts files.inspect
21
- files.each do |file|
22
- sh "ruby #{file}"
19
+ require 'rbconfig'
20
+ FileList["test/*_test.rb"].each do |file|
21
+ # To allow multiple ruby installs (like a multiruby test suite), I need to get
22
+ # the exact ruby binary that's linked to the ruby running the Rakefile. Just saying
23
+ # "ruby" will find the system's installed ruby and be worthless
24
+ ruby = File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"])
25
+ sh "#{ruby} #{file}"
23
26
  end
24
27
  end
25
28
 
@@ -37,14 +40,11 @@ PROJECT_WEB_PATH = "/var/www/gforge-projects/rbplusplus"
37
40
  namespace :web do
38
41
  desc "Build website"
39
42
  task :build => :rdoc do |t|
40
- cd "website" do
41
- sh "webgen"
42
- end
43
-
44
43
  unless File.directory?("publish")
45
44
  mkdir "publish"
46
45
  end
47
- sh "cp -r website/output/* publish/"
46
+
47
+ sh "jekyll --pygment website publish/"
48
48
  sh "cp -r html/* publish/rbplusplus/"
49
49
  end
50
50
 
@@ -63,7 +63,7 @@ spec = Gem::Specification.new do |s|
63
63
  s.name = "rbplusplus"
64
64
  s.version = RBPLUSPLUS_VERSION
65
65
  s.summary = 'Ruby library to generate Rice wrapper code'
66
- s.homepage = 'http://rbplusplus.rubyforge.org/rbplusplus'
66
+ s.homepage = 'http://rbplusplus.rubyforge.org'
67
67
  s.rubyforge_project = "rbplusplus"
68
68
  s.author = 'Jason Roelofs'
69
69
  s.email = 'jameskilton@gmail.com'
@@ -73,8 +73,8 @@ Rb++ combines the powerful query interface of rbgccxml and the Rice library to
73
73
  make Ruby wrapping extensions easier to write than ever.
74
74
  END
75
75
 
76
- s.add_dependency "rbgccxml", "0.8"
77
- s.add_dependency "rice", ">= 1.0.2"
76
+ s.add_dependency "rbgccxml", "~> 0.9"
77
+ s.add_dependency "rice", "~> 1.2.0"
78
78
 
79
79
  patterns = [
80
80
  'TODO',
data/TODO CHANGED
@@ -1,45 +1,13 @@
1
- TODO
2
- * Figure out how to fix the error when running rake test
3
- * Allowing use of "have_library"
4
- * Other mkmf functions?
1
+ Parts to move into rbgccxml:
5
2
 
6
- * rbgccxml: Continue working with QueryResult#find, making it as powerful
7
- and robust as possible.
3
+ - Class purely_virtual? (check the abstract attribute)
4
+ - Namespace#methods ?
8
5
 
9
- * Rest of the GCCXML types (as needed):
10
- - ArrayType
11
- - Converter
12
- - Destructor
13
- - Field
14
- - FunctionType
15
- - OperatorFunction
16
- - OperatorMethod
17
- - OffsetType
18
- - Union
19
- - Variable
6
+ Move documentation to YARD?
20
7
 
21
- STARTED
22
- * Adding constructor
23
- - Test constructor arguments
24
-
25
- DONE
26
- * Adding -I entries
27
- * Adding -L and -l entries
28
- * Adding classes to global (define_class)
29
- * Adding modules
30
- * Adding methods to modules (define_module_function)
31
- * Adding classes to modules / other classes (define_class_under)
32
- * Adding methods to classes (.define_method)
33
- * Adding classes to classes (ad infinitum)
34
- * Look into file naming when dealing with Modules
35
- * Nested Modules
36
- * Recognizing and building to_ruby / from_ruby methods for const type references
37
- * Moving the to_ruby / from_ruby definitions to a place that all will know about them
38
- * Enumerations
39
- * Proper TypesManager dealings with single_file writer mode
40
- * Working with the defined C++ heirarchy chain
41
-
42
- No longer waiting on rice - just building a wrapper myself now.
43
- * Adding global methods (Kernel-level methods)
44
- * Adding class-level methods (define_singleton_method)
8
+ Use sdoc?
45
9
 
10
+ Logging:
11
+ - Method overloads, wrap what a method is getting named as
12
+ - More detail into the writing, multiple and single file writers
13
+ - More debug messages in the building process
@@ -0,0 +1,57 @@
1
+ module RbPlusPlus
2
+ module Builders
3
+
4
+ # Handles code generation for telling Rice how to allocate / deallocate
5
+ # classes. See ClassNode#check_allocation_strategies.
6
+ class AllocationStrategyNode < Base
7
+
8
+ def initialize(parent, code, has_public_constructor, has_public_destructor)
9
+ super(code, parent)
10
+ @public_constructor = has_public_constructor
11
+ @public_destructor = has_public_destructor
12
+ end
13
+
14
+ # Used by MultipleFileWriter to only wrap a given type once.
15
+ def qualified_name
16
+ "#{self.code.qualified_name}_AllocStrat"
17
+ end
18
+
19
+ def build
20
+ end
21
+
22
+ def write
23
+ includes << "#include <rice/Allocation_Strategies.hpp>"
24
+
25
+ node_name = self.code.qualified_name
26
+ code = <<-END
27
+ namespace Rice {
28
+ template<>
29
+ struct Default_Allocation_Strategy< #{node_name} > {
30
+ static #{node_name} * allocate();
31
+ static void free(#{node_name} * obj);
32
+ };
33
+ }
34
+ END
35
+
36
+ declarations << code
37
+
38
+ pre = "Rice::Default_Allocation_Strategy< #{node_name} >::"
39
+
40
+ tmp = "#{node_name} * #{pre}allocate() { return "
41
+ tmp += @public_constructor ? "new #{node_name};" : "NULL;"
42
+ tmp += " }"
43
+
44
+ registrations << tmp
45
+
46
+ tmp = "void #{pre}free(#{node_name} * obj) { "
47
+ tmp += @public_destructor ? "delete obj;" : ""
48
+ tmp += " }"
49
+
50
+ registrations << tmp
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
57
+
@@ -1,256 +1,159 @@
1
1
  module RbPlusPlus
2
2
  module Builders
3
3
 
4
- # Top class for all source generation classes. A builder has three seperate
5
- # code "parts" to fill up for the source writer:
4
+ # Base class for all code generation nodes
6
5
  #
7
- # includes
8
- # declarations
9
- # body
6
+ # A Node is simply a handler for one complete statement or block of C++ code.
10
7
  #
11
- # includes:
12
- # The list of #include's needed for this builder's code to compile
13
- #
14
- # declarations:
15
- # Any extra required functions or class declarations that will be defined
16
- # outside of the main body of the code
17
- #
18
- # body:
19
- # The body is the code that will go in the main control function
20
- # of the file. For extensions, it's Init_extension_name() { [body] }.
21
- # For classes it's usually a register_ClassName() { [body] }, and so on.
22
- #
23
- # All builders can access their parent and add pieces of code to any of these
24
- # three parts
8
+ # The code generation system for Rb++ is a two step process.
9
+ # We first, starting with an ExtensionNode, build up an internal representation
10
+ # of the resulting code, setting up all the code nodes required for proper
11
+ # wrapping of the library.
25
12
  #
13
+ # Once that's in place, then we run through the tree, actually generating
14
+ # the C++ wrapper code.
26
15
  class Base
27
16
 
28
- attr_reader :name, :node
29
-
30
- # Any given builder has a list of sub-builders of any type
31
- attr_accessor :builders
32
-
33
- # Builders need to constcuct the following code parts
34
- #
35
- # The list of includes this builder needs
17
+ # List of includes for this node
36
18
  attr_accessor :includes
37
- # The list of declarations to add
19
+
20
+ # List of declaration nodes for this node
38
21
  attr_accessor :declarations
39
- # The body code
40
- attr_accessor :body
41
22
 
42
- # Link to the parent builder who created said builder
23
+ # List of registeration nodes for this node
24
+ attr_accessor :registrations
25
+
26
+ # Link to the parent node of this node
43
27
  attr_accessor :parent
44
28
 
45
- # The name of the C++ variable related to this builder.
29
+ # Link to the underlying rbgccxml node this node is writing code for
30
+ attr_accessor :code
31
+
32
+ # List of children nodes
33
+ attr_accessor :nodes
34
+
35
+ # List of children nodes that generate code that the entire extension
36
+ # needs to be able to read. Code that fits here includes any auto-generated
37
+ # to_/from_ruby and any Allocation Strategies
38
+ attr_accessor :global_nodes
39
+
40
+ # The Rice variable name for this node
46
41
  attr_accessor :rice_variable
42
+
43
+ # The type of the rice_variable
47
44
  attr_accessor :rice_variable_type
48
45
 
49
- # Create a new builder.
50
- def initialize(name, parser)
51
- @name = name
52
- @node = parser
53
- @builders = []
46
+ def initialize(code, parent = nil)
47
+ @code = code
48
+ @parent = parent
54
49
  @includes = []
55
50
  @declarations = []
56
- @body = []
57
- @registered_nodes = []
51
+ @registrations = []
52
+ @nodes = []
53
+ @global_nodes = []
58
54
  end
59
-
60
- # adds a register function to the Init or register of this node
61
- def register_node(node, register_func)
62
- @registered_nodes << [node, register_func]
63
- end
64
-
65
- private
66
-
67
- def nested_level(node, level=0)
68
- return level if node.is_a?(RbGCCXML::Namespace) || node.is_a?(RbGCCXML::Enumeration)
69
- return level if node.super_classes.length == 0
70
- node.super_classes.each do |sup|
71
- level = nested_level(sup, level+1)
72
- end
73
- return level
55
+
56
+ # Does this builder node have child nodes?
57
+ def has_children?
58
+ @nodes && !@nodes.empty?
74
59
  end
75
-
76
- public
77
-
78
- # Sorts the registered nodes by hierachy, registering the base classes
79
- # first.
80
- #
81
- # this is necessary for Rice to know about inheritance
82
- def registered_nodes
83
- #sort by hierachy
84
- nodes = @registered_nodes.sort_by do |build, func|
85
- if build.node.nil?
86
- 0
87
- else
88
- nested_level(build.node)
60
+
61
+ # Trigger the construction of the internal representation of a given node.
62
+ # All nodes must implement this.
63
+ def build
64
+ raise "Nodes must implement #build"
65
+ end
66
+
67
+ # After #build has run, this then triggers the actual generation of the C++
68
+ # code and returns the final string.
69
+ # All nodes must implement this.
70
+ def write
71
+ raise "Nodes must implement #write"
72
+ end
73
+
74
+ # Once building is done, the resulting node tree needs to be sorted according
75
+ # to subclass / superclass definitions. Like anything with C++, Rice needs to
76
+ # know about base classes before it can build sub classes. We go through
77
+ # each node's children, sorting them according to this.
78
+ def sort
79
+ @nodes.each { |n| n.sort }
80
+
81
+ # sort_by lets us build an array of numbers that Ruby then uses
82
+ # to sort the list. Our method here is to simply specify the
83
+ # depth a given class is in a heirarchy, as bigger numbers end
84
+ # up sorted farther down the list
85
+ @nodes =
86
+ @nodes.sort_by do |a|
87
+ a.is_a?(ClassNode) ? superclass_count(a.code) : 0
89
88
  end
90
- end
91
-
92
- #collect the sorted members
93
- nodes.collect do |node, func|
94
- func
95
- end
96
- end
97
-
98
- # The name of the header file to include
99
- # This is the file default, so long as it matches one of the export files
100
- # If not this returns all exported files.
101
- #
102
- # This was added to workaround badly declared namespaces
103
- def header_files(node)
104
- file = node.file_name(false)
105
- return [file] if self.class.sources.include?(file)
106
- self.class.sources
107
- end
108
-
109
- # Adds the necessary includes in order to compile the specified node
110
- def add_includes_for(node)
111
- header_files(node).each do |header|
112
- includes << "#include \"#{header}\""
113
- end
114
- end
115
-
116
- # Include any user specified include files
117
- def add_additional_includes
118
- self.class.additional_includes.each do |inc|
119
- includes << "#include \"#{inc}\""
120
- end
121
- end
122
-
123
- # Set a list of user specified include files
124
- def self.additional_includes=(addl)
125
- @@additional_includes = addl
126
- end
127
-
128
- # Get an array of user specified include files
129
- def self.additional_includes
130
- @@additional_includes || []
131
- end
132
-
133
- # A list of all the source files. This is used in order to prevent files
134
- # that are not in the list from being included and mucking things up
135
- def self.sources=(sources)
136
- @@sources = sources
137
- end
138
-
139
- # Retrieves a list of user specified source files
140
- def self.sources
141
- @@sources || []
142
89
  end
143
90
 
144
- # All builders must implement this method
145
- def build
146
- raise "Builder needs to implement #build"
91
+ # Proxy method for writers
92
+ def qualified_name
93
+ self.code.qualified_name
147
94
  end
148
95
 
149
- # Builders should use to_s to make finishing touches on the generated
150
- # code before it gets written out to a file.
151
- def to_s
152
- extras = []
153
- #Weird trailing } needs to be destroyed!!!
154
- if self.body.flatten[-1].strip == "}"
155
- extras << self.body.delete_at(-1)
96
+ protected
97
+
98
+ # Count the heirarchy depth of a given class node
99
+ def superclass_count(node)
100
+ count = 0
101
+ n = node
102
+ while n = n.superclass
103
+ count += 1
156
104
  end
157
-
158
- return [
159
- self.includes.flatten.uniq,
160
- "",
161
- self.declarations,
162
- "",
163
- self.body,
164
- "",
165
- self.registered_nodes,
166
- extras
167
- ].flatten.join("\n")
105
+ count
168
106
  end
169
107
 
170
- # Get the full qualified name of the related gccxml node
171
- def qualified_name
172
- @node.qualified_name
108
+ # Should this node be wrapped as it is or has the user
109
+ # specified something else for this node?
110
+ def do_not_wrap?(node)
111
+ node.ignored? || (node.moved_to && node.moved_to != self.code) || !node.public?
173
112
  end
174
113
 
175
- # Register all classes
176
- def build_classes(classes = nil)
177
- classes ||= [@node.classes, @node.structs].flatten
178
- classes.each do |klass|
179
- next if klass.ignored? || klass.moved?
180
- next unless klass.public?
181
- builder = ClassBuilder.new(self, klass)
182
- builder.build
183
- builders << builder
184
- end
114
+ # Given a new node, build it and add it to our nodes list
115
+ def add_child(node)
116
+ node.build
117
+ nodes << node
185
118
  end
186
119
 
187
- # Find and wrap up all enumerations
188
- def build_enumerations
189
- @node.enumerations.each do |enum|
190
- builder = EnumerationBuilder.new(self, enum)
191
- builder.build
192
- builders << builder
193
- end
120
+ # Add a node to the "globals" list. See the declaration of global_nodes
121
+ def add_global_child(node)
122
+ node.build
123
+ global_nodes << node
194
124
  end
195
125
 
196
- # Compatibility with Rice 1.0.1's explicit self requirement, build a quick
197
- # wrapper that includes a self and discards it, forwarding the call as needed.
126
+ # Any node can also have a typedef. There are cases where it's
127
+ # much better to use a typedef instead of the original fully qualified
128
+ # name, for example deep template definitions (say, any STL structures).
129
+ # This method will look for the best Typedef to use for this node.
130
+ # This lookup can be disabled on a per-node basis by calling node.disable_typedef_lookup,
131
+ # as there are cases where the typedef search works against the parsed code.
198
132
  #
199
- # Returns: the name of the wrapper function
200
- def build_function_wrapper(function, append="")
201
- return if function.ignored? || function.moved?
202
- wrapper_func = "wrap_#{function.qualified_name.gsub(/::/, "_")}#{append}"
133
+ # @returns the typedef node found or the node passed in (self.code by default)
134
+ def find_typedef(node = self.code)
135
+ return node if node.is_a?(RbGCCXML::FundamentalType)
203
136
 
204
- proto_string = ["Rice::Object self"]
205
- call_string = []
206
- function.arguments.map{|arg| [arg.cpp_type.to_s(true), arg.name]}.each do |parts|
207
- type = parts[0]
208
- name = parts[1]
209
- proto_string << "#{type} #{name}"
210
- call_string << "#{name}"
211
- end
137
+ found = last_found = node
212
138
 
213
- proto_string = proto_string.join(",")
214
- call_string = call_string.join(",")
215
- return_type = function.return_type.to_s(true)
216
- returns = "" if return_type == "void"
217
- returns ||= "return"
139
+ if !node._disable_typedef_lookup?
140
+ while found
141
+ last_found = found
142
+ typedef = RbGCCXML::XMLParsing.find(:node_type => "Typedef", :type => found.attributes["id"])
218
143
 
219
- declarations << "#{return_type} #{wrapper_func}(#{proto_string}) {"
220
- declarations << "\t#{returns} #{function.qualified_name}(#{call_string});"
221
- declarations << "}"
144
+ # Some typedefs have the access attribute, some don't. We want those without the attribute
145
+ # and those with the access="public". For this reason, we can't put :access => "public" in the
146
+ # query above.
147
+ found = (typedef && typedef.public?) ? typedef : nil
148
+ end
149
+ end
222
150
 
223
- wrapper_func
151
+ last_found
224
152
  end
225
153
 
226
- # Compatibility with Rice 1.0.1's method overloading issues. Build a quick
227
- # wrapper that includes a self, forwarding the call as needed.
228
- #
229
- # Returns: the name of the wrapper function
230
- def build_method_wrapper(cls, method, i)
231
- return if method.ignored? || method.moved?
232
- wrapper_func = "wrap_#{method.qualified_name.functionize}_#{i}"
233
- proto_string = ["#{cls.qualified_name} *self"]
234
- call_string = []
235
- method.arguments.map{|arg| [arg.cpp_type.to_s(true), arg.name]}.each do |parts|
236
- type = parts[0]
237
- name = parts[1]
238
- proto_string << "#{type} #{name}"
239
- call_string << "#{name}"
240
- end
241
-
242
- proto_string = proto_string.join(",")
243
- call_string = call_string.join(",")
244
- return_type = method.return_type.to_s(true)
245
- returns = "" if return_type == "void"
246
- returns ||= "return"
247
-
248
- declarations << "#{return_type} #{wrapper_func}(#{proto_string}) {"
249
- declarations << "\t#{returns} self->#{method.qualified_name}(#{call_string});"
250
- declarations << "}"
251
-
252
- wrapper_func
253
- end
154
+ alias find_typedef_for find_typedef
155
+
254
156
  end
157
+
255
158
  end
256
159
  end