cplus2ruby 1.0.0
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.
- 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
|