examples/sample.rb
#!/usr/bin/env ruby
=begin
Sample for CGenerator.
==version
CGenerator 0.14
The current version of this software can be found at
((<"http://redshift.sourceforge.net/cgen
"|URL:http://redshift.sourceforge.net/cgen>)).
==license
This software is distributed under the Ruby license.
See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
==author
Joel VanderWerf,
((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
=end
require 'cgen/cgen'
require 'fileutils'
FileUtils.mkdir_p "tmp"
lib = CGenerator::Library.new "sample_lib"
class Point; end
lib.declare_extern_struct(:point).instance_eval {
# make it extern so we can see it from another lib
declare :x => "double x"
declare :y => "double y"
}
lib.define_c_global_function(:new_point).instance_eval {
arguments "x", "y" # 'VALUE' is assumed
declare :p => "point *p"
declare :result => "VALUE result"
# semicolons are added automatically
body %{
result = Data_Make_Struct(#{declare_module Point}, point, 0, free, p);
p->x = NUM2DBL(x);
p->y = NUM2DBL(y);
//# might want to do something like this, too:
//# rb_funcall(result, #{lib.declare_symbol :initialize}, 0);
}
returns "result"
# can put a return statement in the body, if preferred
}
for var in [:x, :y] # metaprogramming in C!
lib.define_c_method(Point, var).instance_eval {
declare :p => "point *p"
body %{
Data_Get_Struct(self, point, p);
}
returns "rb_float_new(p->#{var})"
}
end
# A utility function, available to other C files
lib.define_c_function("distance").instance_eval {
arguments "point *p1", "point *p2"
return_type "double"
scope :extern
returns "sqrt(pow(p1->x - p2->x, 2) + pow(p1->y - p2->y, 2))"
include "<math.h>"
# The include accumulator call propagates up the parent
# hierarchy until something handles it. In this case,
# the Library lib handles it by adding an include
# directive to the .c file. This allows related, but
# separate aspects of the C source to be handled in
# the same place in the Ruby code. We could also have
# called include directly on lib.
}
lib.define_c_method(Point, :distance).instance_eval {
# no name conflict between this "distance" and the previous one,
# because "method" and "Point" are both part of the C identifier
# for this method
arguments "other"
declare :p => "point *p"
declare :q => "point *q"
body %{
Data_Get_Struct(self, point, p);
Data_Get_Struct(other, point, q);
}
returns "rb_float_new(distance(p, q))"
}
lib.commit # now you can use the new definitions
p1 = new_point(1, 2)
puts "p1: x is #{p1.x}, y is #{p1.y}"
p2 = new_point(5, 8)
puts "p2: x is #{p2.x}, y is #{p2.y}"
puts "distance from p1 to p2 is #{p1.distance p2}"
# now let's make another lib, and test Library#c_array_args
lib2 = CGenerator::Library.new "sample_lib_2"
lib2.include "../sample_lib/sample_lib.h"
lib2.define_c_method(Point, :closest).instance_eval {
# 'farthest Q_1, Q_2, ...'
# returns the Q_i which is closest to self
c_array_args # args get passed in argc, argv
declare :i => "int i",
:dist => "double dist",
:mindist => "double mindist",
:p => "point *p",
:q => "point *q",
:result => "VALUE result"
body %{
result = Qnil;
Data_Get_Struct(self, point, p);
for (i = 0; i < argc; i++) {
Data_Get_Struct(argv[i], point, q);
dist = distance(p, q);
if (i == 0 || dist < mindist) {
mindist = dist;
result = argv[i];
}
}
}
returns "result"
}
lib2.define_c_singleton_method(Point, :test).instance_eval {
c_array_args {
required :arg0, :arg1
optional :arg2, :arg3, :arg4
typecheck :arg2 => Numeric, :arg3 => Numeric
default :arg3 => "INT2NUM(7)",
:arg4 => "INT2NUM(NUM2INT(arg2) + NUM2INT(arg3))"
rest :rest
block :block
}
body %{\
rb_funcall(block, #{declare_symbol :call}, 6,
arg0, arg1, arg2, arg3, arg4, rest);
}
# returns nil by default
}
lib2.commit
puts
p3 = new_point(-2, 5)
q = new_point(4.1, 4.2)
puts "q=(#{q.x}, #{q.y})"
r = q.closest(p1, p2, p3)
for p in [p1, p2, p3]
puts "point #{p.x}, #{p.y}. Distance to q: #{q.distance(p)}"
end
puts "closest to q: (#{r.x}, #{r.y})"
puts
tester = proc { |arg0, arg1, arg2, arg3, arg4, rest|
argstrs = [arg0, arg1, arg2, arg3, arg4, rest].map { |arg| arg.inspect }
printf "test args are: %s, %s, %s, %s, %s, %s\n", *argstrs
}
Point.test(0, 1, 2, &tester) # omit 2 ==> Ruby fails to convert nil to int.
Point.test(0, 1, 2, 3, &tester)
Point.test(0, 1, 2, 3, 4, &tester)
Point.test(0, 1, 2, 3, 4, 5, &tester)
Point.test(0, 1, 2, 3, 4, 5, 6, &tester)
Generated by GNU enscript 1.6.4.