rbind 0.0.22 → 0.0.23

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.
@@ -10,6 +10,7 @@ module Rbind
10
10
  class << self
11
11
  attr_accessor :default_type_names
12
12
  attr_accessor :default_type_alias
13
+ attr_accessor :default_operator_alias
13
14
  end
14
15
  # TODO move somewhere else
15
16
  self.default_type_names = [:int,:int8,:int32,:int64,:uint,:uint8,:uint32,:uint64,
@@ -22,6 +23,30 @@ module Rbind
22
23
  :u_long_long => :ulong_long,:u_int => :uint, :uint128 => :uint128,
23
24
  :char_s => :char}
24
25
 
26
+ self.default_operator_alias = {
27
+ "()" => "fct",
28
+ "!=" => "unequal",
29
+ "==" => "equal",
30
+ "&=" => "and_set",
31
+ "+=" => "add",
32
+ "-=" => "sub",
33
+ "++" => "plusplus",
34
+ "--" => "minusminus",
35
+ "[]" => "array",
36
+ "<=" => "less_or_equal",
37
+ "<" => "less",
38
+ ">=" => "greater_or_equal",
39
+ "+" => "plus",
40
+ "-" => "minus",
41
+ "*" => "mult",
42
+ "/" => "div",
43
+ "!" => "not",
44
+ "&" => "and",
45
+ ">" => "greater",
46
+ "=" => "assign"
47
+ }
48
+
49
+
25
50
  attr_reader :operations
26
51
  attr_reader :operation_alias
27
52
  attr_reader :consts
@@ -222,12 +247,12 @@ module Rbind
222
247
  op.alias
223
248
  elsif op.alias
224
249
  name = "#{op.alias}#{@operations[op.name].size+1}"
225
- ::Rbind.log.debug "name clash: aliasing #{op.alias} --> #{name}"
250
+ ::Rbind.log.debug "add_operation: name clash: aliasing #{op.alias} --> #{name}"
226
251
  name
227
252
  else
228
253
  op.auto_alias = true
229
254
  name = "#{op.name}#{@operations[op.name].size+1}"
230
- ::Rbind.log.debug "name clash: #{op.name} --> #{name}"
255
+ ::Rbind.log.debug "add_operation: name clash: #{op.name} --> #{name}"
231
256
  name
232
257
  end
233
258
  op.index = @operations[op.name].size
@@ -308,11 +333,7 @@ module Rbind
308
333
  else
309
334
  type.owner = self
310
335
  if type.alias
311
- if check_exist && type(type.alias,false,false)
312
- raise ArgumentError,"A type with the name alias #{type.alias} already exists"
313
- end
314
- raise ArgumentError,"A type alias with the name #{t.alias} already exists" if(t = @type_alias[type.alias])
315
- @type_alias[type.alias] = type.to_raw
336
+ add_type_alias(type, type.alias, check_exist)
316
337
  end
317
338
  raise ArgumentError,"A type with the name #{t.full_name} already exists" if(t = @types[type.name])
318
339
  @types[type.name] = type.to_raw
@@ -377,7 +398,8 @@ module Rbind
377
398
  t ||= if search_owner && name =~ /([:\w]*)<(.*)>$/
378
399
  t = type($1,false) if $1 && !$1.empty?
379
400
  t2 = type($2,false) if $2 && !$2.empty?
380
- if t && t2
401
+ # template is known to this library
402
+ if t && t2
381
403
  name = "#{t.name}<#{t2.full_name}>"
382
404
  t3 ||= t.owner.type(name,false,false)
383
405
  t3 ||= begin
@@ -396,9 +418,15 @@ module Rbind
396
418
  end
397
419
  end
398
420
  end
421
+ # generic template is unknown
422
+ # check if specialised template is known
423
+ elsif t2 && $1 && !$1.empty?
424
+ real_name = "#{$1}<#{t2.full_name}>".gsub(">>","> >")
425
+ type(real_name,false) if(real_name != name) # prevent stack level too deep
426
+ else
427
+ nil
399
428
  end
400
429
  end
401
-
402
430
  if !t && raise_
403
431
  if self.class.callbacks_for_hook(:on_type_not_found)
404
432
  results = self.run_hook(:on_type_not_found,self,name)
@@ -484,6 +512,28 @@ module Rbind
484
512
  end
485
513
  end
486
514
 
515
+ # Adding a type alias in the main namespace, e.g.
516
+ # to register existing typedefs
517
+ #
518
+ # raises if the alias is already register under a given typename
519
+ def add_type_alias(known_type, alias_name, check_exist = true)
520
+ if check_exist && type_alias.has_key?(alias_name)
521
+ existing_type = type(alias_name, false, false)
522
+ if known_type.name == existing_type.name
523
+ ::Rbind.log.warn "Alias: '#{alias_name} for type '#{known_type.name}' already registered'"
524
+ else
525
+ raise ArgumentError, "Cannot register alias: #{alias_name} for type '#{known_type.name}'. Alias already registered with type: '#{existing_type.name}'"
526
+ end
527
+ else
528
+ ::Rbind.log.debug "Alias: '#{alias_name} for type '#{known_type.name}' registered"
529
+ @type_alias[alias_name] = known_type.to_raw
530
+ end
531
+ end
532
+
533
+ def self.default_operators
534
+ default_operator_alias.keys
535
+ end
536
+
487
537
  def method_missing(m,*args)
488
538
  if m != :to_ary && args.empty?
489
539
  t = type(m.to_s,false,false)
@@ -1,6 +1,8 @@
1
1
 
2
2
  module Rbind
3
3
  class ROperation < RBase
4
+ extend ::Rbind::Logger
5
+
4
6
  attr_accessor :return_type
5
7
  attr_accessor :parameters
6
8
  attr_accessor :cparameters
@@ -89,6 +91,7 @@ module Rbind
89
91
  end
90
92
 
91
93
  def generate_signatures
94
+ ROperation.log.debug "ROperation: generate signature for #{return_type}: #{return_type.signature}" unless constructor?
92
95
  s = "#{return_type.signature} " unless constructor?
93
96
  s = "#{s}#{full_name}(#{parameters.map(&:signature).join(", ")})"
94
97
 
@@ -22,8 +22,14 @@ module Rbind
22
22
  end
23
23
 
24
24
  class RTemplateClass < RClass
25
+ extend ::Rbind::Logger
26
+
27
+ # template parameters in the right order
28
+ attr_reader :template_parameters
29
+
25
30
  def initialize(name,*parent_classes)
26
31
  raise "parent classes for template classes are not supported!" if !parent_classes.empty?
32
+ @template_parameters =[]
27
33
  super
28
34
  end
29
35
 
@@ -39,11 +45,67 @@ module Rbind
39
45
 
40
46
  # hook for implementing the specialization
41
47
  def specialize(klass,*parameters)
48
+ RTemplateClass.log.info "RTemplateClass: #{name} default specialized with #{klass} -- specialization parameters: #{parameters}, template parameters: #{template_parameters}"
49
+ # by default create a dummy implementation without additional arguments
50
+ if parameters.size != @template_parameters.size
51
+ raise ArgumentError, "RTemplateClass: template #{name} expects #{@template_parameters.size} parameters"
52
+ end
53
+
54
+ operations.each do |ops|
55
+ ops.each do |o|
56
+ begin
57
+ RTemplateClass.log.debug "RTemplateClass: #{name} handle operation: #{o.name}"
58
+ op = o.dup
59
+
60
+ if op.kind_of?(RGetter) or op.kind_of?(RSetter)
61
+ attr = RAttribute.new(op.attribute.name, resolve_type(parameters, op.attribute.type))
62
+ op = op.class.new(attr)
63
+ klass.add_operation(op)
64
+ next
65
+ else
66
+ op.return_type = resolve_type(parameters, op.return_type.name)
67
+ end
68
+
69
+ op.parameters.each do |p|
70
+ rtype = resolve_type(parameters, p.type.name)
71
+ RTemplateClass.log.debug "RTemplateClass: #{name} specialized with #{klass} -- resolved #{p.type.name} -> #{rtype}"
72
+ p.type = rtype
73
+ end
74
+
75
+ klass.add_operation(op)
76
+ rescue Exception => e
77
+ RTemplateClass.log.warn "RTemplateClass: #{name} could not add parameter #{e} #{e.backtrace}"
78
+
79
+ end
80
+ end
81
+ end
82
+ klass
83
+ end
84
+
85
+ def resolve_type(parameters, param_name)
86
+ # This resolves
87
+ resolved_type = type(param_name, false)
88
+ if not resolved_type
89
+ raise ArgumentError, "RTemplateClass: template #{name} could not resolve template parameter #{param_name}"
90
+ end
91
+
92
+ if index = @template_parameters.index(resolved_type)
93
+ resolved_type = type(parameters[index])
94
+ end
95
+
96
+ resolved_type
42
97
  end
43
98
 
44
99
  # hook for generating additional ruby code going to be embedded into the
45
100
  # class definition
46
101
  def specialize_ruby_specialization(klass)
47
102
  end
103
+
104
+ def add_type(klass)
105
+ if klass.kind_of?(RTemplateParameter)
106
+ @template_parameters << klass
107
+ end
108
+ super
109
+ end
48
110
  end
49
111
  end
@@ -56,5 +56,14 @@ module Rbind
56
56
  str + s
57
57
  end
58
58
  end
59
+
60
+ # Resolve the current delegate to the underlying object
61
+ def get_base_delegate
62
+ obj = __getobj__
63
+ while obj.respond_to?(:__getobj__)
64
+ obj = obj.__getobj__
65
+ end
66
+ obj
67
+ end
59
68
  end
60
69
  end
@@ -92,7 +92,13 @@ module Rbind
92
92
  default = unmask_template(array.join(" "))
93
93
  type = find_type(owner,type_name)
94
94
  flags = normalize_flags(line_number,flags,:IO,:O)
95
- type = if flags.include?(:O) || flags.include?(:IO) || type.basic_type?
95
+ type = if flags.include?(:O) || flags.include?(:IO)
96
+ if type.ptr?
97
+ type
98
+ else
99
+ type.to_ref # the opencv parser encodes references as flags
100
+ end
101
+ elsif type.basic_type?
96
102
  type
97
103
  else
98
104
  type.to_const
@@ -232,13 +238,20 @@ module Rbind
232
238
  end
233
239
 
234
240
  ns = RBase.namespace(name)
235
- owner = type(ns,true)
241
+ owner = type(ns,false)
242
+ owner ||= add_namespace_name(ns)
236
243
  if return_type_name == "explicit"
237
244
  flags << return_type_name
238
245
  return_type_name = nil
239
246
  end
247
+ flags = normalize_flags(line_number,flags,:S,:O)
240
248
  return_type = if return_type_name && !return_type_name.empty?
241
- find_type(owner,return_type_name)
249
+ t = find_type(owner,return_type_name)
250
+ if !t.ptr? && flags.include?(:O)
251
+ t.to_ref
252
+ else
253
+ t
254
+ end
242
255
  end
243
256
  line_counter = 1
244
257
  args = a.map do |line|
@@ -248,7 +261,6 @@ module Rbind
248
261
  end
249
262
  op = ::Rbind::ROperation.new(name,return_type,*args)
250
263
  op.alias = alias_name if alias_name && !alias_name.empty?
251
- flags = normalize_flags(line_number,flags,:S)
252
264
  op = if flags.include?(:S)
253
265
  op.to_static
254
266
  else
@@ -194,6 +194,10 @@ module Rbind
194
194
  if operator? && parameters.size == 1
195
195
  if return_type.basic_type?
196
196
  "return *rbind_obj_ #{operator} #{paras};"
197
+ elsif return_type.ref?
198
+ # the returned value is not the owner of the
199
+ # object
200
+ "return toC(&(*rbind_obj_ #{operator} #{paras}),false);"
197
201
  else
198
202
  "return toC(new #{return_type.full_name}(*rbind_obj_ #{operator} #{paras}));"
199
203
  end
@@ -201,6 +205,10 @@ module Rbind
201
205
  "return #{full_name}(#{paras});"
202
206
  elsif return_type.ptr?
203
207
  "return toC(#{full_name}(#{paras}));"
208
+ elsif return_type.ref?
209
+ # the returned value is not the owner of the
210
+ # object
211
+ "return toC(&#{full_name}(#{paras}),false);"
204
212
  else
205
213
  "return toC(new #{return_type.full_name}(#{full_name}(#{paras})));"
206
214
  end
@@ -117,7 +117,7 @@ module Rbind
117
117
 
118
118
 
119
119
  def self.normalize_type_name(name)
120
- name = name.gsub(" ","")
120
+ name.gsub!(" ","")
121
121
 
122
122
  # map template classes
123
123
  # std::vector<std::string> -> Std::Vector::Std_String
@@ -133,6 +133,12 @@ module Rbind
133
133
  return n if n
134
134
  end
135
135
 
136
+ # Parse constant declaration with suffix like 1000000LL
137
+ if name =~ /^([0-9]+)[uUlL]{0,2}/
138
+ name = $1
139
+ return name
140
+ end
141
+
136
142
  # map all uint ... to Fixnum
137
143
  if name =~ /^u?int\d*$/ || name =~ /^u?int\d+_t$/
138
144
  return "Fixnum"
@@ -168,28 +174,31 @@ module Rbind
168
174
 
169
175
  def self.normalize_alias_method_name(orig_name)
170
176
  name = orig_name
177
+
171
178
  #replace operatorX with the correct ruby operator when
172
179
  #there are overloaded operators
173
180
  name = if name =~/^operator(.*)/
174
181
  n = $1
175
182
  if n =~ /\(\)/
176
183
  raise "forbbiden method name #{name}"
177
- elsif n=~ /(.*)(\d)/
178
- if $1 == "[]"
179
- "array_operator#{$2}"
180
- elsif $1 == "+"
181
- "plus_operator#{$2}"
182
- elsif $1 == "-"
183
- "minus_operator#{$2}"
184
- elsif $1 == "*"
185
- "mul_operator#{$2}"
186
- elsif $1 == "/"
187
- "div_operator#{$2}"
188
- else
189
- raise "forbbiden method name #{name}"
184
+ elsif n =~ /(.*)(\d)/ # check for overloaded operators and use alias name for them
185
+ alias_name = RNamespace.default_operator_alias[$1]
186
+ if not alias_name
187
+ raise ArgumentError, "Normalization failed. Operator: #{$1} unknown"
190
188
  end
189
+ "#{alias_name}_operator#{$2}"
191
190
  else
192
- n
191
+ # this operators does not exist
192
+ if n == "++" || n == "--"
193
+ alias_name = RNamespace.default_operator_alias[n]
194
+ if not alias_name
195
+ raise ArgumentError, "Normalization failed. Operator: #{$1} unknown"
196
+ end
197
+ "#{alias_name}_operator"
198
+ else
199
+ # we can use the c++ name
200
+ n
201
+ end
193
202
  end
194
203
  else
195
204
  name
@@ -214,11 +223,21 @@ module Rbind
214
223
  #remove all remaining #
215
224
  name = name.gsub(/#/, '')
216
225
  name = normalize_alias_method_name(name)
217
- raise "generated empty name for #{orig_name}" if name.empty?
226
+ raise RuntimeError, "Normalization failed: generated empty name for #{orig_name}" if name.empty?
227
+ name
228
+ end
229
+
230
+ def self.normalize_enum_name(name)
231
+ name = GeneratorRuby.normalize_basic_type_name_ffi name
232
+ #to lower and substitute namespace :: with _
233
+ name = name.gsub("::","_")
234
+ name = name.downcase
218
235
  name
219
236
  end
220
237
 
221
238
  class HelperBase
239
+ extend ::Rbind::Logger
240
+
222
241
  attr_accessor :name
223
242
  def initialize(name,root)
224
243
  @name = name.to_s
@@ -259,6 +278,10 @@ module Rbind
259
278
  GeneratorRuby.normalize_basic_type_name_ffi name
260
279
  end
261
280
 
281
+ def normalize_enum(name)
282
+ GeneratorRuby.normalize_enum_name(name)
283
+ end
284
+
262
285
  def normalize_m(name)
263
286
  GeneratorRuby.normalize_method_name name
264
287
  end
@@ -292,32 +315,59 @@ module Rbind
292
315
  return_type = if op.constructor?
293
316
  "#{normalize_t op.owner.full_name}"
294
317
  else
295
- if op.return_type.basic_type?
296
- if op.return_type.ptr?
318
+ op_return_type = op.return_type
319
+ if op_return_type.respond_to?(:get_base_delegate)
320
+ if klass = op_return_type.get_base_delegate
321
+ op_return_type = klass if klass.kind_of?(REnum)
322
+ end
323
+ end
324
+
325
+ if op_return_type.basic_type?
326
+ if op_return_type.ptr?
297
327
  ":pointer"
328
+ elsif op_return_type.kind_of?(REnum)
329
+ ":#{normalize_enum op_return_type.to_raw.csignature}"
298
330
  else
299
- ":#{normalize_bt op.return_type.to_raw.csignature}"
331
+ ":#{normalize_bt op_return_type.to_raw.csignature}"
300
332
  end
301
333
  else
302
- if op.return_type.extern_package_name
303
- normalize_t("::#{op.return_type.extern_package_name}::#{op.return_type.to_raw.full_name}")
334
+ if op_return_type.extern_package_name
335
+ normalize_t("::#{op_return_type.extern_package_name}::#{op_return_type.to_raw.full_name}")
336
+ elsif op_return_type.kind_of?(REnum)
337
+ ":#{normalize_enum op_return_type.to_raw.full_name}"
304
338
  else
305
- normalize_t op.return_type.to_raw.full_name
339
+ normalize_t op_return_type.to_raw.full_name
306
340
  end
307
341
  end
308
342
  end
309
343
  args = op.cparameters.map do |p|
310
- if p.type.basic_type?
311
- if p.type.ptr?
344
+ p_type = p.type
345
+ if p_type.respond_to?(:get_base_delegate)
346
+ if klass = p_type.get_base_delegate
347
+ p_type = klass if klass.kind_of?(REnum)
348
+ end
349
+ end
350
+ if p_type.basic_type?
351
+ if p_type.ptr? || p.type.ref?
312
352
  ":pointer"
353
+ elsif p.type.kind_of?(REnum)
354
+ # Includes enums, which need to be defined accordingly
355
+ # using ffi:
356
+ # enum :normalized_name, [:first, 1,
357
+ # :second,
358
+ # :third]
359
+ #
360
+ ":#{normalize_enum p.type.to_raw.csignature}"
313
361
  else
314
362
  ":#{normalize_bt p.type.to_raw.csignature}"
315
363
  end
316
364
  else
317
- if p.type.extern_package_name
318
- normalize_t("::#{p.type.extern_package_name}::#{p.type.to_raw.full_name}")
365
+ if p_type.extern_package_name
366
+ normalize_t("::#{p_type.extern_package_name}::#{p_type.to_raw.full_name}")
367
+ elsif p_type.kind_of?(REnum)
368
+ ":#{normalize_enum p_type.to_raw.full_name}"
319
369
  else
320
- normalize_t p.type.to_raw.full_name
370
+ normalize_t p_type.to_raw.full_name
321
371
  end
322
372
  end
323
373
  end
@@ -330,6 +380,25 @@ module Rbind
330
380
  str+"\n"
331
381
  str.gsub(/\n/,"\n ")
332
382
  end
383
+
384
+ def add_enums
385
+ str = "\n"
386
+ @root.root.each_type do |t|
387
+ if t.kind_of?(REnum)
388
+ str += "\tenum :#{GeneratorRuby::normalize_enum_name(t.to_raw.csignature)}, ["
389
+ t.values.each do |name,value|
390
+ if value
391
+ str += ":#{name},#{value}, "
392
+ else
393
+ str += ":#{name}, "
394
+ end
395
+ end
396
+ str += "]\n\n"
397
+ end
398
+ end
399
+ str
400
+ end
401
+
333
402
  end
334
403
 
335
404
  class RTypeTemplateHelper < HelperBase
@@ -451,7 +520,11 @@ module Rbind
451
520
  def generate_param_doc
452
521
  paras = parameters.map do |p|
453
522
  n = GeneratorRuby.normalize_arg_name p.name
454
- t = GeneratorRuby.normalize_type_name(p.type.full_name)
523
+ t = if p.type.basic_type? && (p.type.ptr? || p.type.ref?)
524
+ "FFI::MemoryPointer"
525
+ else
526
+ GeneratorRuby.normalize_type_name(p.type.full_name)
527
+ end
455
528
  "# @param [#{t}] #{n} #{p.doc}"
456
529
  end
457
530
  if return_type
@@ -583,7 +656,12 @@ module Rbind
583
656
  def add_consts(root=@root)
584
657
  str = @root.consts.map do |c|
585
658
  next if c.extern? || c.ignore?
659
+ if not c.default_value
660
+ HelperBase.log.warn "#{c.name}: no default value"
661
+ next
662
+ else
586
663
  " #{c.name} = #{GeneratorRuby::normalize_type_name(c.default_value)}\n"
664
+ end
587
665
  end.join
588
666
  return str unless @compact_namespace
589
667
 
@@ -610,31 +688,39 @@ module Rbind
610
688
  h[k] = Array.new
611
689
  end
612
690
  root.each_operation do |o|
613
- next if o.constructor? || o.ignore?
614
- op = OperationHelper.new(o)
615
- if op.instance_method?
616
- ops["rbind_instance_#{op.name}"] << op
617
- else
618
- ops["rbind_static_#{op.name}"] << op
691
+ begin
692
+ next if o.constructor? || o.ignore?
693
+ op = OperationHelper.new(o)
694
+ if op.instance_method?
695
+ ops["rbind_instance_#{op.name}"] << op
696
+ else
697
+ ops["rbind_static_#{op.name}"] << op
698
+ end
699
+ rescue Exception => e
700
+ HelperBase.log.warn "Operation '#{o}' not added. #{e}"
619
701
  end
620
702
  end
621
703
  # render method
622
704
  str = ""
623
705
  ops.each_value do |o|
624
- if o.size == 1
625
- op = o.first
626
- str += if op.instance_method?
627
- @method_wrapper.result(op.binding)
628
- else
629
- @static_method_wrapper.result(op.binding)
630
- end
631
- else
632
- helper = OverloadedOperationHelper.new(o)
633
- str += if o.first.instance_method?
634
- @overloaded_method_wrapper.result(helper.binding)
635
- else
636
- @overloaded_static_method_wrapper.result(helper.binding)
637
- end
706
+ begin
707
+ if o.size == 1
708
+ op = o.first
709
+ str += if op.instance_method?
710
+ @method_wrapper.result(op.binding)
711
+ else
712
+ @static_method_wrapper.result(op.binding)
713
+ end
714
+ else
715
+ helper = OverloadedOperationHelper.new(o)
716
+ str += if o.first.instance_method?
717
+ @overloaded_method_wrapper.result(helper.binding)
718
+ else
719
+ @overloaded_static_method_wrapper.result(helper.binding)
720
+ end
721
+ end
722
+ rescue Exception => e
723
+ HelperBase.log.warn "Operation '#{o}' could not be rendered. #{e}"
638
724
  end
639
725
  end
640
726
  return str unless @compact_namespace