jameskilton / rbplusplus

Use rbgccxml and rice to automatically generate C++ Ruby extensions.

This URL has Read+Write access

rbplusplus / README
100644 351 lines (243 sloc) 11.707 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
== What is rb++?
 
Rb++ makes it almost trivially easy to create Ruby extensions for any C
or C++ library / code. In the simplest of cases, there is no need to ever
touch C, everything is done in a very simple and clean Ruby API.
 
Note: For those familiar with py++, the similarities are minimal.
Outside of the purpose of both libraries, rb++ was built from scratch to
provide a Ruby-esque query and wrapping API instead of being a port. However,
many thanks to Roman for his work, the major inspiration for this library.
 
== Requirements
 
* rbgccxml
* rice (http://rice.rubyforge.org)
 
== Installation
 
Rice builds and installs on any *nix system, including Mac OS X and Cygwin. Rice,
and therefor rb++ will not work on Windows outside of Cygwin.
 
Everything is installed with a simple line:
 
  gem install rbplusplus
 
== The Project
 
For bug reports, patch submissions, project annoucements and downloads, visit rb++'s rubyforge
project page at:
 
http://www.rubyforge.org/projects/rbplusplus
 
Feel free to post help request, hints, or general ideas on the forums.
 
Rb++'s source is in a git repository hosted on github:
 
Project page:
 
http://github.com/jameskilton/rbgplusplus/tree/master
 
Clone with:
 
  git clone git://github.com/jameskilton/rbplusplus.git
 
== Getting Started
 
All rb++ projects start off with the Extension class:
 
  require 'rbplusplus'
  include RbPlusPlus
 
  Extension.new "extension_name"
 
The one requirement on the C++ code for rb++ to easily handle it, is that the code that's
to be wrapped is in its own namespace. If the code to be wrapped is in the global namespace,
then you should build a seperate header file that includes all the files to be wrapped
inside of a namespace:
 
  namespace to_wrap {
    #include "file1.h"
    #include "file2.h"
    #include "file3.h"
    ...
  }
 
Extension has two ways of being used: block syntax for simple projects and immediate
syntax for more control over the whole process.
 
=== Block Mode
 
For basic reading and wrapping needs, the block syntax makes rb++ very easy to write and
read
 
  Extension.new "extension" do |e|
    ...
  end
 
=== Immediate Mode
 
For those that want more fine-grained control over the parsing / building / writing / compiling
process, immediate syntax is also available
 
  e = Extension.new "extension"
  ...
  e.build # => Generates the C++ code
  e.write # => Writes out to files
  e.compile # => Compiles into an extension
 
Please note the ##build ##write and ##compile methods. These are required for an extension to be
built. These calls are made automatically in Block Mode. See the RbPlusPlus::Extension class
for more details.
 
== Basic Usage
 
For the most basic usage, where there are C++ header files to wrap and it's simple enough
to not need extra processing, there are only two required calls: Extension.sources and
Extension.namespace. Extension.sources has a few ways to be called (and is much the same as RbGCCXML.parse):
 
  # A single header file
  Extension.new "extension" do |e|
    e.sources "/path/to/header.h"
  end
 
  # An array of header files
  Extension.new "extension" do |e|
    e.sources ["/path/to/header.h", "/path/there.h"]
  end
 
  # A glob
  Extension.new "extension" do |e|
    e.sources "/path/to/*.h"
  end
 
  # An array of globs
  Extension.new "extension" do |e|
    e.sources ["/path/to/*.h", "/elsewhere/*.hpp"]
  end
 
One special method that's also required in the Immediate Mode is Extension#working_dir=. This
specifies where rb++ will put the generated code. In Block Mode, the default is to put the code
in __FILE__/generated, but as rb++ cannot ascertain the __FILE__ information without a block,
it will need to be stated explicitly. Use this method in Block Mode if the default location does
not work.
 
  e = Extension.new "extension"
  e.working_dir = "/path/to/generate/files/"
 
The last required method is Extension#namespace. As mentioned above, all extensions
are built from code in a given C++ namespace. That namespace needs to be specified before
Rb++ will start any processing
 
  # Wrap all code under the 'to_wrap' namespace
  Extension.new "extension" do |e|
    e.namespace "to_wrap"
  end
 
There is one place where Extension#namespace isn't exactly required: when you'll be wrapping up
the C++ code soley in other Ruby modules to be contained in the extension.
 
The general rule is this: <b>If you want C++ code wrapped, you must use Extension#namespace to specify
which code should go where</b>.
 
== More Detailed Usage
 
Because C++ does not easily wrap into Ruby code for many reasons, rb++ is much more capable than just
the basic usage above. There are many different features available to help define and build the wrapper.
 
=== Modules
 
An extension can include 0..n modules in which code will be wrapped. Any given module needs to be given
a ##namespace call. This defines which C++ code will be wrapped into this module.
 
  Extension.new "extension" do |e|
    e.sources ...
    # If there is no global-space code to be wrapped
    # #namespace is not required here
 
    e.module "MyModule" do |m|
      # We want to wrap all code in ::my_module into this ruby module
      m.namespace "my_module"
    end
  end
 
=== Particularly hairy APIs
 
It's well known that source code may not follow a very clean format, or even be internally consistent.
When dealing with such problems -- code that just won't adhere to a wrappable format -- rb++ opens up a slew
of manipulation routines for controlling exactly what gets wrapped, where it gets wrapped, and how
it gets wrapped.
 
==== Excluding / Ignoring
 
Often times you will want to ignore a method on an object, a whole class, or a whole namespace even. This
can be useful if the function you wish to ignore takes a 'void *' as an argument, or for a variety of other
reasons.
You can tell rb++ which namespaces/classes/methods to ignore very easily:
 
  Extension.new "extension" do |e|
    e.sources ...
    node = e.namespace "Physics"
    node.classes("Callback").ignore # Ignore classes named Callback
    node.classes("Shape").methods("registerCallback").ignore # Ignore the method Shape::registerCallback
  end
 
You can also ignore a whole query result with the same notation:
 
 
  Extension.new "extension" do |e|
    ...
    node.methods.find(:all, :name => "free").ignore # Ignores all class methods named 'free'
    ...
  end
 
==== Including
 
If a C++ library has a certain class in an undesired namespace, or a certain function with undesired visibility
you can easily fix this by using the declarative 'include'.
 
  Extension.new "extension" do |e|
    e.sources ...
    node = e.namespace "PhysicsMath"
 
    e.module "Physics" do |m|
      m.module "Math" do |math|
        #moves each class to Physics::Math
        node.classes.each do |c|
          math.includes c
        end
      end
    end
 
  end
 
Note that when you include something in a module it is moved from it's original location. In the example above
the classes will only exist in Physics::Math and not PhysicsMath
 
==== Renaming
 
Sometimes C++ libraries implement certain architectures that are nice to have in C++, but are terrible in Ruby. For
example, many older libraries in C++ start name their classes with a "C#{class}".
 
In order to rectify this you use the #wrap_as method to rename the node:
 
  Extension.new "extension" do |e|
    e.sources ...
    node = e.namespace "Physics"
    node.classes("CShape").wrap_as("Shape")
    node.classes("CShape").methods("hasCollided").wrap_as("collided?")
  end
 
==== Function / Method Conversions
 
C++ APIs can also sometimes put as global functions functionality you want contained in a class or module. This
kind of wrapping is also trivially easy in rb++. Say you have the function:
 
  inline int mod(int a, int b) {
    return a%b;
  }
 
and you want to add it to your Math class:
 
  mod = node.functions("mod")
  node.classes("Math").includes mod.as_instance_method
 
Please note the #as_instance_method. You now have Math#mod in your extension
 
  require 'extension'
 
  Math.new.mod(1, 2)
 
== Possible 'Gotchas'
 
=== Constructor overloading
 
A current limitation in rice currently does not allow for more than one constructor to be exported. This
will not be a limitation in future versions of Rice, but for now make sure that only one constructor is wrapped.
This can be done via direct constructor access:
 
  node.classes("MyClass").constructors[0].ignore
 
or by querying according to the arguments of the constructor(s) you want to ignore:
 
  node.classes("MyClass").constructors.find(:arguments => [nil, nil]) # ignore constructors with 2 arguments
 
=== Method overloading
 
Method overloading is supported, but not by Rice. Therefore all overloaded methods are wrapped in the order
that they are presented to gccxml. For example:
 
  class System {
    public:
    System() {}
    inline void puts(std::string s) { std::cout << s << std::endl; }
    inline void puts() { puts(""); }
  };
 
can be used by default in rb++ like so:
 
  s = System.new
  s.puts_0("Hello world")
  s.puts_1
 
You can, however, rename them as you see fit if you tell rb++ how, for example:
 
  puts_methods = node.classes("System").methods("puts") # Gives 2 puts methods back
  puts_methods[0].wrap_as("puts")
 
After doing this you can use the methods as follows:
 
  s = System.new
  s.puts("Hello World")
  s.puts_1
 
=== Methods with optional arguments
 
Rice does not currently support default arguments. Right now they are all required. For example:
 
  int times(int a=0, int b=0) {
    return a*b;
  }
 
can only be invoked with 2 arguments. If you are having problems with this, please consult the rb++ forum.
 
=== Additional notes
 
* Method / function names are underscored by default. So <tt>YourClass::doSomething</tt> becomes <tt>YourClass#do_something</tt>
 
== Misc Options
 
=== File Writing Options
 
By default, rb++ will write out the extension in multiple files, following the convention of
 
  extension_name.cpp
  _ClassName.rb.hpp
  _ClassName.rb.cpp
  _ModuleName_ClassName.rb.hpp
  _ModuleName_ClassName.rb.cpp
  ...
 
This is done to prevent obscenely long compile times, super large code files, or uncompilable extensions due to
system limitations (e.g. RAM) that are common with big SWIG projects.
 
Rb++ can also write out the extension code in a single file (extension_name.cpp) with Extension#writer_mode
 
  Extension.new "extension" do |e|
    e.writer_mode :single
  end
 
=== Compilation options
 
rb++ takes care of setting up the extension to be properly compiled, but sometimes certain
compiler options can't be deduced. rb++ has options to specify library paths (-L), libraries (-l),
and include paths (-I) to add to the compilation lines, as well as just adding your own flags
directly to the command line. These are options on Extension.sources
 
  Extension.new "extension" do |e|
    e.sources *header_dirs,
      :library_paths => *paths, # Adds to -L
      :libraries => *libs, # Adds to -l
      :include_paths => *includes, # Adds to -I
      :cxxflags => *flags, # For those flags that don't match the above three
      :ldflags => *flags, # For extra linking flags that don't match the above
      :includes => *files, # For when there are header files that need to be included into the
                                      # compilation but *don't* get parsed out and wrapped
      :include_source_files => *files # A list of source files that will get copied into working_dir and
                                      # compiled with the extension
  end
 
Any compiler errors and the full build log will be found in rbpp_compile.log.