cplus2ruby 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +210 -0
- data/cplus2ruby.gemspec +19 -0
- data/example.rb +44 -0
- data/lib/cplus2ruby/code_generator.rb +52 -0
- data/lib/cplus2ruby/compiler.rb +84 -0
- data/lib/cplus2ruby/cpp_code_generator.rb +287 -0
- data/lib/cplus2ruby/model.rb +212 -0
- data/lib/cplus2ruby/typing.rb +152 -0
- data/lib/cplus2ruby/wrapper_code_generator.rb +165 -0
- data/lib/cplus2ruby.rb +30 -0
- data/test/test_mixin.rb +29 -0
- data/test/work/Makefile +149 -0
- data/test/work/test_mixin.cc +23 -0
- data/test/work/test_mixin.h +51 -0
- data/test/work/test_mixin.o +0 -0
- data/test/work/test_mixin.so +0 -0
- data/test/work/test_mixin_wrap.cc +105 -0
- data/test/work/test_mixin_wrap.o +0 -0
- metadata +82 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'facets/annotations'
|
2
|
+
require 'facets/orderedhash'
|
3
|
+
require 'cplus2ruby/typing'
|
4
|
+
|
5
|
+
class Cplus2Ruby::Property; end
|
6
|
+
class Cplus2Ruby::Method; end
|
7
|
+
|
8
|
+
class Cplus2Ruby::Model
|
9
|
+
attr_reader :typing
|
10
|
+
attr_reader :code
|
11
|
+
attr_reader :includes
|
12
|
+
|
13
|
+
def next_order_cnt
|
14
|
+
@order_cnt += 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@typing = Cplus2Ruby::Typing.new
|
19
|
+
@code = ""
|
20
|
+
@includes = []
|
21
|
+
@settings = default_settings()
|
22
|
+
@order_cnt = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def finish!
|
26
|
+
entities.each do |klass|
|
27
|
+
@typing.add_object_type(klass)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def entities
|
32
|
+
entities = []
|
33
|
+
ObjectSpace.each_object(Class) {|o|
|
34
|
+
entities << o if o.kind_of?(Cplus2Ruby::Entity)
|
35
|
+
}
|
36
|
+
entities
|
37
|
+
end
|
38
|
+
|
39
|
+
def entity_usage(klass, other)
|
40
|
+
usage_cnt = 0
|
41
|
+
klass.local_annotations.each do |name, opts|
|
42
|
+
usage_cnt += 1 if opts[:arguments] and opts[:arguments].values.include?(other)
|
43
|
+
usage_cnt += 1 if opts[:type] and opts[:type] == other
|
44
|
+
end
|
45
|
+
usage_cnt
|
46
|
+
end
|
47
|
+
|
48
|
+
def entities_ordered
|
49
|
+
entities().sort {|a, b|
|
50
|
+
if a.ancestors.include?(b)
|
51
|
+
# a 'after' b (a > b)
|
52
|
+
1
|
53
|
+
elsif b.ancestors.include?(a)
|
54
|
+
-1
|
55
|
+
else
|
56
|
+
ea = entity_usage(a, b)
|
57
|
+
eb = entity_usage(b, a)
|
58
|
+
|
59
|
+
if ea > 0 and eb == 0
|
60
|
+
-1
|
61
|
+
elsif eb > 0 and ea == 0
|
62
|
+
1
|
63
|
+
else
|
64
|
+
ao = (a.heritage(:__options__) || {})[:order] || 0
|
65
|
+
bo = (b.heritage(:__options__) || {})[:order] || 0
|
66
|
+
ao <=> bo
|
67
|
+
end
|
68
|
+
end
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Update or retrieve the current settings.
|
74
|
+
#
|
75
|
+
def settings(h={})
|
76
|
+
@settings.update(h)
|
77
|
+
@settings
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def default_settings
|
83
|
+
{
|
84
|
+
:substitute_iv_ats => true,
|
85
|
+
:default_body_when_nil => ''
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
module Cplus2Ruby
|
92
|
+
#
|
93
|
+
# Global code
|
94
|
+
#
|
95
|
+
def self.<<(code)
|
96
|
+
model.code << "\n"
|
97
|
+
model.code << code
|
98
|
+
model.code << "\n"
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.include(header)
|
102
|
+
model.includes << header
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.settings(h={})
|
106
|
+
model.settings(h)
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.model
|
110
|
+
@model ||= Cplus2Ruby::Model.new
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.add_type_alias(h)
|
114
|
+
h.each {|from, to| model.typing.alias_entry(from, to)}
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.startup(*args, &block)
|
118
|
+
self.model.finish!
|
119
|
+
Cplus2Ruby::Compiler.new(self.model).startup(*args, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.compile(*args)
|
123
|
+
self.model.finish!
|
124
|
+
Cplus2Ruby::Compiler.new(self.model).compile(*args)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
module Cplus2Ruby::Entity
|
130
|
+
def property(name, type=Object, options={})
|
131
|
+
raise ArgumentError if options[:type]
|
132
|
+
options[:type] = type
|
133
|
+
ann! name, Cplus2Ruby::Property, options
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# method :name, {hash}, {hash}, ..., {hash}, body, hash
|
138
|
+
#
|
139
|
+
def method(name, *args)
|
140
|
+
options = parse_method_args(args)
|
141
|
+
ann! name, Cplus2Ruby::Method, options
|
142
|
+
end
|
143
|
+
|
144
|
+
def static_method(name, *args)
|
145
|
+
options = parse_method_args(args)
|
146
|
+
options[:static] = true
|
147
|
+
ann! name, Cplus2Ruby::Method, options
|
148
|
+
end
|
149
|
+
|
150
|
+
def stub_method(name, *args)
|
151
|
+
options = parse_method_args(args)
|
152
|
+
options[:stub] = true
|
153
|
+
ann! name, Cplus2Ruby::Method, options
|
154
|
+
end
|
155
|
+
|
156
|
+
alias method_c method
|
157
|
+
|
158
|
+
def virtual(*virtuals)
|
159
|
+
virtuals.each do |name|
|
160
|
+
ann! name, :virtual => true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
protected
|
165
|
+
|
166
|
+
def parse_method_args(args)
|
167
|
+
params = OrderedHash.new
|
168
|
+
body = nil
|
169
|
+
options = {}
|
170
|
+
|
171
|
+
state = :want_param
|
172
|
+
|
173
|
+
while not args.empty?
|
174
|
+
arg = args.shift
|
175
|
+
|
176
|
+
case state
|
177
|
+
when :want_param
|
178
|
+
if arg.is_a?(Hash)
|
179
|
+
params.update(arg)
|
180
|
+
else
|
181
|
+
args.unshift(arg)
|
182
|
+
state = :want_body
|
183
|
+
end
|
184
|
+
when :want_body
|
185
|
+
body = arg
|
186
|
+
state = :want_options
|
187
|
+
when :want_options
|
188
|
+
raise unless arg.is_a?(Hash)
|
189
|
+
raise unless args.empty?
|
190
|
+
options = arg
|
191
|
+
else
|
192
|
+
raise
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
options[:body] ||= body
|
197
|
+
options[:arguments] ||= params
|
198
|
+
|
199
|
+
return options
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
class Module
|
206
|
+
def cplus2ruby(hash={})
|
207
|
+
include Cplus2Ruby::Entity
|
208
|
+
extend Cplus2Ruby::Entity
|
209
|
+
ann! :__options__, hash
|
210
|
+
ann! :__options__, :order => Cplus2Ruby.model.next_order_cnt if hash[:order].nil?
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
class Cplus2Ruby::Typing
|
2
|
+
require 'facets/orderedhash'
|
3
|
+
|
4
|
+
attr_reader :aliases
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@map = default_map()
|
8
|
+
@aliases = OrderedHash.new
|
9
|
+
clone_entry Object, 'VALUE'
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_entry(type, entry)
|
13
|
+
type = to_key(type)
|
14
|
+
raise if @map.include?(type)
|
15
|
+
@map[type] = entry
|
16
|
+
end
|
17
|
+
|
18
|
+
def clone_entry(from, to)
|
19
|
+
from = to_key(from)
|
20
|
+
to = to_key(to)
|
21
|
+
raise if @map.include?(from)
|
22
|
+
@map[from] = @map[to] # FIXME: .dup?
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Add a type alias. Also modifies type map.
|
27
|
+
#
|
28
|
+
def alias_entry(from, to)
|
29
|
+
from = to_key(from)
|
30
|
+
to = to_key(to)
|
31
|
+
@aliases[from] = to
|
32
|
+
clone_entry(from, to)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_entry(type)
|
36
|
+
@map[to_key(type)]
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Looks up first in the annotation options, then
|
41
|
+
# in the type options.
|
42
|
+
#
|
43
|
+
def lookup_entry(attribute, options, type)
|
44
|
+
(get_entry(type) || {}).dup.update(options)[attribute]
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Returns a C++ declaration
|
49
|
+
#
|
50
|
+
def var_decl(type, name)
|
51
|
+
if entry = get_entry(type)
|
52
|
+
entry[:ctype].gsub("%s", name.to_s)
|
53
|
+
# FIXME type.to_s
|
54
|
+
elsif type.to_s.include?("%s")
|
55
|
+
type.gsub("%s", name.to_s)
|
56
|
+
else
|
57
|
+
"#{type} #{name}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def var_assgn(name, value)
|
62
|
+
raise ArgumentError if value.nil?
|
63
|
+
if value.is_a?(String) and value.include?('%s')
|
64
|
+
value.gsub('%s', name)
|
65
|
+
else
|
66
|
+
"#{name} = #{value}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Returns true if Ruby <-> C conversion for this type is possible
|
72
|
+
#
|
73
|
+
def can_convert?(type)
|
74
|
+
get_entry(type) ? true : false
|
75
|
+
end
|
76
|
+
|
77
|
+
def convert(type, var, kind)
|
78
|
+
(get_entry(type)[kind] || "").gsub('%s', var.to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_object_type(klass)
|
82
|
+
add_entry(klass, object_type(klass.name))
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def to_key(type)
|
88
|
+
if type.is_a?(Symbol)
|
89
|
+
type.to_s
|
90
|
+
else
|
91
|
+
type
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def object_type(type)
|
96
|
+
{
|
97
|
+
:init => "NULL",
|
98
|
+
:mark => "if (%s) rb_gc_mark(%s->__obj__)",
|
99
|
+
:ruby2c => "(NIL_P(%s) ? NULL : (#{type}*)DATA_PTR(%s))",
|
100
|
+
:c2ruby => "(%s ? %s->__obj__ : Qnil)",
|
101
|
+
:ctype => "#{type} *%s",
|
102
|
+
:ruby2c_checktype => "if (!NIL_P(%s)) Check_Type(%s, T_DATA)"
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def default_map
|
107
|
+
{
|
108
|
+
'VALUE' => {
|
109
|
+
:init => 'Qnil',
|
110
|
+
:mark => 'rb_gc_mark(%s)',
|
111
|
+
:ruby2c => '%s',
|
112
|
+
:c2ruby => '%s',
|
113
|
+
:ctype => 'VALUE %s'
|
114
|
+
},
|
115
|
+
'float' => {
|
116
|
+
:init => 0.0,
|
117
|
+
:ruby2c => '(float)NUM2DBL(%s)',
|
118
|
+
:c2ruby => 'rb_float_new((double)%s)',
|
119
|
+
:ctype => 'float %s'
|
120
|
+
},
|
121
|
+
'double' => {
|
122
|
+
:init => 0.0,
|
123
|
+
:ruby2c => '(double)NUM2DBL(%s)',
|
124
|
+
:c2ruby => 'rb_float_new(%s)',
|
125
|
+
:ctype => 'double %s'
|
126
|
+
},
|
127
|
+
'int' => {
|
128
|
+
:init => 0,
|
129
|
+
:ruby2c => '(int)NUM2INT(%s)',
|
130
|
+
:c2ruby => 'INT2NUM(%s)',
|
131
|
+
:ctype => 'int %s'
|
132
|
+
},
|
133
|
+
'unsigned int' => {
|
134
|
+
:init => 0,
|
135
|
+
:ruby2c => '(unsigned int)NUM2INT(%s)',
|
136
|
+
:c2ruby => 'INT2NUM(%s)',
|
137
|
+
:ctype => 'unsigned int %s'
|
138
|
+
},
|
139
|
+
'bool' => {
|
140
|
+
:init => false,
|
141
|
+
:ruby2c => '(RTEST(%s) ? true : false)',
|
142
|
+
:c2ruby => '(%s ? Qtrue : Qfalse)',
|
143
|
+
:ctype => 'bool %s'
|
144
|
+
},
|
145
|
+
'void' => {
|
146
|
+
:c2ruby => 'Qnil',
|
147
|
+
:ctype => 'void'
|
148
|
+
}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'cplus2ruby/code_generator'
|
2
|
+
|
3
|
+
class Cplus2Ruby::WrapperCodeGenerator < Cplus2Ruby::CodeGenerator
|
4
|
+
def gen_allocator(klass)
|
5
|
+
%[
|
6
|
+
static VALUE
|
7
|
+
#{klass.name}_alloc__(VALUE klass)
|
8
|
+
{
|
9
|
+
#{klass.name} *__cobj__;
|
10
|
+
__cobj__ = new #{klass.name}();
|
11
|
+
__cobj__->__obj__ = Data_Wrap_Struct(klass, RubyObject::__mark, RubyObject::__free, __cobj__);
|
12
|
+
return __cobj__->__obj__;
|
13
|
+
}
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def gen_method_wrapper(klass, name, options)
|
18
|
+
gen_wrapper(klass, name, options, :wrap)
|
19
|
+
end
|
20
|
+
|
21
|
+
def gen_property_getter(klass, name, options)
|
22
|
+
opts = options.dup
|
23
|
+
opts[:arguments] = {:returns => options[:type]}
|
24
|
+
gen_wrapper(klass, name, opts, :get)
|
25
|
+
end
|
26
|
+
|
27
|
+
def gen_property_setter(klass, name, options)
|
28
|
+
opts = options.dup
|
29
|
+
opts[:arguments] = {:__val__ => options[:type]}
|
30
|
+
gen_wrapper(klass, name, opts, :set)
|
31
|
+
end
|
32
|
+
|
33
|
+
def gen_property_accessor(klass, name, options)
|
34
|
+
[gen_property_getter(klass, name, options),
|
35
|
+
gen_property_setter(klass, name, options)].join
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# kind is one of :set, :get, :wrap
|
40
|
+
#
|
41
|
+
def gen_wrapper(klass, name, options, kind)
|
42
|
+
args = options[:arguments].dup
|
43
|
+
return nil if options[:stub]
|
44
|
+
unless args_convertable?(args)
|
45
|
+
STDERR.puts "WARN: cannot wrap method #{klass.name}::#{name} (#{kind})"
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
returns = args.delete(:returns) || "void"
|
50
|
+
|
51
|
+
s = ([["__self__", "VALUE"]] + args.to_a).map {|n,_| "VALUE #{n}"}.join(", ")
|
52
|
+
|
53
|
+
out = ""
|
54
|
+
out << "static VALUE\n"
|
55
|
+
out << "#{klass.name}_#{kind}__#{name}(#{s})\n"
|
56
|
+
out << "{\n"
|
57
|
+
|
58
|
+
# declare C++ return value
|
59
|
+
if returns != 'void'
|
60
|
+
out << @model.typing.var_decl(returns, '__res__') + ";\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
# declare C++ object reference
|
64
|
+
out << "#{klass.name} *__cobj__;\n"
|
65
|
+
#out << @model.var_decl(klass, '__cobj__') + ";\n"
|
66
|
+
|
67
|
+
# convert __self__ to C++ object reference (FIXME: can remove?)
|
68
|
+
out << "Check_Type(__self__, T_DATA);\n"
|
69
|
+
out << "__cobj__ = (#{klass.name}*) DATA_PTR(__self__);\n"
|
70
|
+
|
71
|
+
# check argument types
|
72
|
+
out << args.map {|n, t| @model.typing.convert(t, n.to_s, :ruby2c_checktype) + ";\n" }.join
|
73
|
+
|
74
|
+
# call arguments
|
75
|
+
call_args = args.map {|n, t| @model.typing.convert(t, n.to_s, :ruby2c)}
|
76
|
+
|
77
|
+
# build method call
|
78
|
+
out << "__res__ = " if returns != 'void'
|
79
|
+
case kind
|
80
|
+
when :wrap
|
81
|
+
out << "__cobj__->#{name}(#{call_args.join(', ')});\n"
|
82
|
+
when :get
|
83
|
+
out << "__cobj__->#{name};\n"
|
84
|
+
when :set
|
85
|
+
raise ArgumentError if call_args.size != 1
|
86
|
+
out << "__cobj__->#{name} = #{call_args.first};\n"
|
87
|
+
else
|
88
|
+
raise ArgumentError
|
89
|
+
end
|
90
|
+
|
91
|
+
# convert return value
|
92
|
+
retval = @model.typing.convert(returns, '__res__', :c2ruby)
|
93
|
+
|
94
|
+
out << "return #{retval};\n"
|
95
|
+
out << "}\n"
|
96
|
+
|
97
|
+
return out
|
98
|
+
end
|
99
|
+
|
100
|
+
def gen_init(mod_name)
|
101
|
+
out = ""
|
102
|
+
out << %[extern "C" void Init_#{mod_name}()\n]
|
103
|
+
out << "{\n"
|
104
|
+
out << " VALUE klass;\n"
|
105
|
+
|
106
|
+
@model.entities_ordered.each do |klass|
|
107
|
+
next if no_wrap?(klass)
|
108
|
+
n = klass.name
|
109
|
+
out << %{ klass = rb_eval_string("#{n}");\n}
|
110
|
+
out << %{ rb_define_alloc_func(klass, #{n}_alloc__);\n}
|
111
|
+
|
112
|
+
all_methods_of(klass) do |name, options|
|
113
|
+
args = options[:arguments]
|
114
|
+
next unless args_convertable?(args)
|
115
|
+
next if options[:stub]
|
116
|
+
out << %{ rb_define_method(klass, "#{name}", }
|
117
|
+
out << %{(VALUE(*)(...))#{n}_wrap__#{name}, #{arity(args)});\n}
|
118
|
+
end
|
119
|
+
|
120
|
+
all_properties_of(klass) do |name, options|
|
121
|
+
next unless @model.typing.can_convert?(options[:type])
|
122
|
+
|
123
|
+
# getter
|
124
|
+
out << %{ rb_define_method(klass, "#{name}", }
|
125
|
+
out << %{(VALUE(*)(...))#{n}_get__#{name}, 0);\n}
|
126
|
+
|
127
|
+
# setter
|
128
|
+
out << %{rb_define_method(klass, "#{name}=", }
|
129
|
+
out << %{(VALUE(*)(...))#{n}_set__#{name}, 1);\n}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
out << "}\n"
|
134
|
+
|
135
|
+
return out
|
136
|
+
end
|
137
|
+
|
138
|
+
def gen_wrapper_file(mod_name)
|
139
|
+
out = ""
|
140
|
+
out << %{#include "#{mod_name}.h"\n\n}
|
141
|
+
|
142
|
+
@model.entities_ordered.each do |klass|
|
143
|
+
next if no_wrap?(klass)
|
144
|
+
|
145
|
+
out << gen_allocator(klass)
|
146
|
+
|
147
|
+
all_methods_of(klass) do |name, options|
|
148
|
+
out << (gen_method_wrapper(klass, name, options) || "")
|
149
|
+
end
|
150
|
+
|
151
|
+
all_properties_of(klass) do |name, options|
|
152
|
+
out << gen_property_accessor(klass, name, options)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
out << gen_init(mod_name)
|
157
|
+
|
158
|
+
return out
|
159
|
+
end
|
160
|
+
|
161
|
+
def write_files(mod_name)
|
162
|
+
write_out(mod_name + "_wrap.cc", gen_wrapper_file(mod_name))
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
data/lib/cplus2ruby.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Cplus2Ruby; end
|
2
|
+
|
3
|
+
require 'cplus2ruby/model'
|
4
|
+
require 'cplus2ruby/compiler'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Extend facets/annotations
|
8
|
+
#
|
9
|
+
class Module
|
10
|
+
def recursive_annotations
|
11
|
+
res = {}
|
12
|
+
ancestors.reverse.each { |ancestor|
|
13
|
+
ancestor.annotations.each { |ref, hash|
|
14
|
+
res[ref] ||= {}
|
15
|
+
res[ref].update(hash) if hash
|
16
|
+
}
|
17
|
+
}
|
18
|
+
res
|
19
|
+
end
|
20
|
+
|
21
|
+
def local_annotations
|
22
|
+
new_keys = recursive_annotations().keys - self.superclass.recursive_annotations().keys
|
23
|
+
all_keys = new_keys + annotations().keys
|
24
|
+
h = {}
|
25
|
+
all_keys.each do |key|
|
26
|
+
h[key] = heritage(key)
|
27
|
+
end
|
28
|
+
h
|
29
|
+
end
|
30
|
+
end
|
data/test/test_mixin.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
$LOAD_PATH.unshift '../src'
|
3
|
+
require 'cplus2ruby'
|
4
|
+
|
5
|
+
module Mixin1; cplus2ruby
|
6
|
+
property :a, :int
|
7
|
+
end
|
8
|
+
|
9
|
+
class A; cplus2ruby
|
10
|
+
property :y, :int
|
11
|
+
end
|
12
|
+
|
13
|
+
class B < A; cplus2ruby
|
14
|
+
include Mixin1
|
15
|
+
end
|
16
|
+
|
17
|
+
class C < B; cplus2ruby
|
18
|
+
property :z, :int
|
19
|
+
end
|
20
|
+
|
21
|
+
Cplus2Ruby.startup('work/test_mixin', true)
|
22
|
+
t = B.new
|
23
|
+
t.y = 2
|
24
|
+
p t.y
|
25
|
+
|
26
|
+
t = C.new
|
27
|
+
t.z = 343333
|
28
|
+
p t.z
|
29
|
+
p t.y
|