rbind 0.0.27 → 0.0.33

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.
@@ -32,8 +32,10 @@ module Rbind
32
32
 
33
33
  #write all types so they get parsed first
34
34
  @root.each_type do |t|
35
- if t.is_a? RClass
35
+ if t.is_a?(RClass)
36
36
  file_extern.write "class #{GeneratorExtern.normalize_type_name(t.full_name)}\n"
37
+ elsif t.is_a?(REnum)
38
+ file_extern.write "enum #{GeneratorExtern.normalize_type_name(t.full_name)}\n"
37
39
  end
38
40
  end
39
41
 
@@ -15,7 +15,7 @@ module Rbind
15
15
  end
16
16
  self.ruby_default_value_map ||= {"true" => "true","TRUE" => "true", "false" => "false","FALSE" => "false"}
17
17
  self.ffi_type_map ||= {"char *" => "string","unsigned char" => "uchar" ,"const char *" => "string","uint8_t" => "uint8",
18
- "uint32_t" => "uint32","uint64_t" => "uint64","int8_t" => "int8","int32_t" => "int32","int64_t" => "int64" }
18
+ "uint16_t" => "uint16","int16_t" => "int16", "uint32_t" => "uint32","uint64_t" => "uint64","int8_t" => "int8","int32_t" => "int32","int64_t" => "int64" }
19
19
 
20
20
 
21
21
  def self.keyword?(name)
@@ -62,8 +62,10 @@ module Rbind
62
62
  val = if parameter.type.basic_type? || parameter.type.ptr?
63
63
  if ruby_default_value_map.has_key?(parameter.default_value)
64
64
  ruby_default_value_map[parameter.default_value]
65
+ elsif parameter.type.is_a? REnum
66
+ ":#{parameter.default_value.split("::").last}"
65
67
  elsif parameter.type.name == "float"
66
- parameter.default_value.gsub("f","").gsub(/\.$/,".0").gsub(/^\./,"0.")
68
+ parameter.default_value.gsub("f","").gsub("F","").gsub(/\.$/,".0").gsub(/^\./,"0.")
67
69
  elsif parameter.type.name == "double"
68
70
  parameter.default_value.gsub(/\.$/,".0").gsub(/^\./,"0.")
69
71
  elsif parameter.type.ptr? &&(parameter.default_value == "0" || parameter.default_value = "NULL")
@@ -131,7 +133,12 @@ module Rbind
131
133
  # map template classes
132
134
  # std::vector<std::string> -> Std::Vector::Std_String
133
135
  if name =~ /([\w:]*)<(.*)>$/
134
- return "#{normalize_type_name($1,true)}::#{normalize_type_name($2,true).gsub("::","_")}"
136
+ name = $1
137
+ names = $2.split(",")
138
+ names.map! do |val|
139
+ normalize_type_name(val,true).gsub("::","_")
140
+ end
141
+ return "#{normalize_type_name(name,true)}::#{names.join("_")}"
135
142
  else
136
143
  name
137
144
  end
@@ -246,6 +253,21 @@ module Rbind
246
253
  name
247
254
  end
248
255
 
256
+ def self.normalize_const_value(name)
257
+ name = name.to_s.gsub(" ","")
258
+ # Parse constant declaration with suffix like 1000000LL
259
+ if name =~ /^([0-9]+)[uUlL]{0,2}/
260
+ name = $1
261
+ return name
262
+ end
263
+ # upcase first letter
264
+ name = name.gsub(/\w*/) do |n|
265
+ next unless n.size > 0
266
+ n[0].upcase+n[1,n.size-1]
267
+ end
268
+ name
269
+ end
270
+
249
271
  class HelperBase
250
272
  extend ::Rbind::Logger
251
273
 
@@ -332,11 +354,10 @@ module Rbind
332
354
  op_return_type = klass if klass.kind_of?(REnum)
333
355
  end
334
356
  end
335
-
336
357
  if op_return_type.basic_type?
337
358
  if op_return_type.ptr?
338
359
  ":pointer"
339
- elsif op_return_type.kind_of?(REnum)
360
+ elsif op.return_type.to_raw.kind_of?(REnum)
340
361
  ":#{normalize_enum op_return_type.to_raw.csignature}"
341
362
  else
342
363
  ":#{normalize_bt op_return_type.to_raw.csignature}"
@@ -344,7 +365,7 @@ module Rbind
344
365
  else
345
366
  if op_return_type.extern_package_name
346
367
  normalize_t("::#{op_return_type.extern_package_name}::#{op_return_type.to_raw.full_name}")
347
- elsif op_return_type.kind_of?(REnum)
368
+ elsif op_return_type.to_raw.kind_of?(REnum)
348
369
  ":#{normalize_enum op_return_type.to_raw.full_name}"
349
370
  else
350
371
  normalize_t op_return_type.to_raw.full_name
@@ -361,7 +382,7 @@ module Rbind
361
382
  if p_type.basic_type?
362
383
  if p_type.ptr? || p.type.ref?
363
384
  ":pointer"
364
- elsif p.type.kind_of?(REnum)
385
+ elsif p.type.to_raw.kind_of?(REnum)
365
386
  # Includes enums, which need to be defined accordingly
366
387
  # using ffi:
367
388
  # enum :normalized_name, [:first, 1,
@@ -375,7 +396,7 @@ module Rbind
375
396
  else
376
397
  if p_type.extern_package_name
377
398
  normalize_t("::#{p_type.extern_package_name}::#{p_type.to_raw.full_name}")
378
- elsif p_type.kind_of?(REnum)
399
+ elsif p_type.to_raw.kind_of?(REnum)
379
400
  ":#{normalize_enum p_type.to_raw.full_name}"
380
401
  else
381
402
  normalize_t p_type.to_raw.full_name
@@ -383,6 +404,7 @@ module Rbind
383
404
  end
384
405
  end
385
406
  fct_name = normalize_m op.cname
407
+ str += "@blocking = true; "if op.blocking?
386
408
  str += "attach_function :#{fct_name},:#{op.cname},[#{args.join(",")}],#{return_type}\n"
387
409
  str
388
410
  end
@@ -399,12 +421,13 @@ module Rbind
399
421
  str += "\tenum :#{GeneratorRuby::normalize_enum_name(t.to_raw.csignature)}, ["
400
422
  t.values.each do |name,value|
401
423
  if value
402
- str += ":#{name},#{value}, "
424
+ value = GeneratorRuby.normalize_const_value(value)
425
+ str += ":#{name},#{GeneratorRuby.normalize_type_name(value)}, "
403
426
  else
404
427
  str += ":#{name}, "
405
428
  end
406
429
  end
407
- str += "]\n\n"
430
+ str += "]\n"
408
431
  end
409
432
  end
410
433
  str
@@ -669,17 +692,21 @@ module Rbind
669
692
  end
670
693
 
671
694
  def add_consts(root=@root)
672
- str = @root.consts.map do |c|
695
+ str = root.consts.map do |c|
673
696
  next if c.extern? || c.ignore?
674
697
  if not c.default_value
675
698
  HelperBase.log.warn "#{c.name}: no default value"
676
699
  next
677
700
  else
678
- " #{c.name} = #{GeneratorRuby::normalize_type_name(c.default_value)}\n"
701
+ val = begin
702
+ eval(c.default_value)
703
+ rescue
704
+ GeneratorRuby::normalize_const_value(c.default_value)
705
+ end
706
+ " #{c.name} = #{val}\n"
679
707
  end
680
708
  end.join
681
709
  return str unless @compact_namespace
682
-
683
710
  root.each_type(false) do |t|
684
711
  next if t.basic_type? && !t.is_a?(RNamespace)
685
712
  str += add_consts(t) if name == GeneratorRuby.normalize_type_name(t.full_name)
@@ -17,7 +17,7 @@ const <%= cname %>* toC(const <%= full_name %>* ptr, bool owner)
17
17
  }
18
18
 
19
19
  // converts const <%= cname %> to const <%= full_name %>
20
- const <%= full_name %>* fromC(const <%= cname %>* ptr)
20
+ const <%= full_name %>* fromC(const <%= cname %>* ptr,bool parse_owner)
21
21
  {
22
22
  if(ptr == NULL)
23
23
  throw std::runtime_error("<%= full_name %>: Null Pointer!");
@@ -38,14 +38,20 @@ const <%= full_name %>* fromC(const <%= cname %>* ptr)
38
38
  // check size
39
39
  if(ptr->size && sizeof(<%= full_name %>) > ptr->size)
40
40
  throw std::runtime_error("wrong object size for <%= full_name %>.");
41
+ if(parse_owner)
42
+ throw std::runtime_error("cannot parse ownerhsip for const parameter for <%= full_name %>.");
41
43
  return static_cast<const <%= full_name %>*>(ptr->obj_ptr);
42
44
  }
43
45
 
44
46
  // converts <%= cname %>* to <%= full_name %>*
45
- <%= full_name %>* fromC(<%= cname %>* ptr)
47
+ <%= full_name %>* fromC(<%= cname %>* ptr,bool parse_owner)
46
48
  {
47
49
  if(ptr == NULL)
48
50
  return NULL;
49
- return const_cast<<%= full_name %>*>(fromC(static_cast<const <%= cname %>*>(ptr)));
51
+ if(parse_owner && !ptr->bowner)
52
+ throw std::runtime_error("Cannot parse ownership. The given object is already owned by someone else <%= full_name %>.");
53
+ if(parse_owner)
54
+ ptr->bowner = false;
55
+ return const_cast<<%= full_name %>*>(fromC(static_cast<const <%= cname %>*>(ptr),false));
50
56
  }
51
57
 
@@ -1,6 +1,6 @@
1
1
  // convert functions for <%= full_name %>* to and from <%= cname %>*
2
2
  const <%= cname %>* toC(const <%= full_name %>* ptr, bool owner = true);
3
3
  <%= cname %>* toC(<%= full_name %>* ptr, bool owner = true);
4
- const <%= full_name %>* fromC(const <%= cname %>* ptr);
5
- <%= full_name %>* fromC(<%= cname %>* ptr);
4
+ const <%= full_name %>* fromC(const <%= cname %>* ptr,bool parse_ownership=false);
5
+ <%= full_name %>* fromC(<%= cname %>* ptr,bool parse_ownership=false);
6
6
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
 
3
3
  from __future__ import print_function
4
- import os, sys, re, string
4
+ import os, sys, re, string, io
5
5
 
6
6
  # the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt
7
7
  opencv_hdr_list = [
@@ -17,27 +17,33 @@ opencv_hdr_list = [
17
17
  "../../objdetect/include/opencv2/objdetect.hpp",
18
18
  "../../imgcodecs/include/opencv2/imgcodecs.hpp",
19
19
  "../../videoio/include/opencv2/videoio.hpp",
20
- "../../highgui/include/opencv2/highgui.hpp"
20
+ "../../highgui/include/opencv2/highgui.hpp",
21
21
  ]
22
22
 
23
23
  """
24
- Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>],
24
+ Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>, original_return_type, docstring],
25
25
  where each element of <list_of_arguments> is 4-element list itself:
26
26
  [argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
27
27
  where the list of modifiers is yet another nested list of strings
28
28
  (currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
29
29
  and "/A value" for the plain C arrays with counters)
30
+ original_return_type is None if the original_return_type is the same as return_value_type
30
31
  """
31
32
 
32
33
  class CppHeaderParser(object):
33
34
 
34
- def __init__(self):
35
+ def __init__(self, generate_umat_decls=False, generate_gpumat_decls=False):
36
+ self._generate_umat_decls = generate_umat_decls
37
+ self._generate_gpumat_decls = generate_gpumat_decls
38
+
35
39
  self.BLOCK_TYPE = 0
36
40
  self.BLOCK_NAME = 1
37
41
  self.PROCESS_FLAG = 2
38
42
  self.PUBLIC_SECTION = 3
39
43
  self.CLASS_DECL = 4
40
44
 
45
+ self.namespaces = set()
46
+
41
47
  def batch_replace(self, s, pairs):
42
48
  for before, after in pairs:
43
49
  s = s.replace(before, after)
@@ -100,6 +106,14 @@ class CppHeaderParser(object):
100
106
  modlist.append("/CA " + macro_arg)
101
107
  arg_str = arg_str[:npos] + arg_str[npos3+1:]
102
108
 
109
+ npos = arg_str.find("const")
110
+ if npos >= 0:
111
+ modlist.append("/C")
112
+
113
+ npos = arg_str.find("&")
114
+ if npos >= 0:
115
+ modlist.append("/Ref")
116
+
103
117
  arg_str = arg_str.strip()
104
118
  word_start = 0
105
119
  word_list = []
@@ -222,7 +236,7 @@ class CppHeaderParser(object):
222
236
  else:
223
237
  prev_val_delta = 0
224
238
  prev_val = val = pv[1].strip()
225
- decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []])
239
+ decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], [], None, ""])
226
240
  return decl
227
241
 
228
242
  def parse_class_decl(self, decl_str):
@@ -246,13 +260,13 @@ class CppHeaderParser(object):
246
260
  l = l[:npos] + l[npos3+1:]
247
261
 
248
262
  l = self.batch_replace(l, [("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("public virtual ", " "), ("public ", " "), ("::", ".")]).strip()
249
- ll = re.split(r'\s*[,:]?\s*', l)
263
+ ll = re.split(r'\s+|\s*[,:]\s*', l)
250
264
  ll = [le for le in ll if le]
251
265
  classname = ll[1]
252
266
  bases = ll[2:]
253
267
  return classname, bases, modlist
254
268
 
255
- def parse_func_decl_no_wrap(self, decl_str, static_method = False):
269
+ def parse_func_decl_no_wrap(self, decl_str, static_method=False, docstring=""):
256
270
  decl_str = (decl_str or "").strip()
257
271
  virtual_method = False
258
272
  explicit_method = False
@@ -295,7 +309,7 @@ class CppHeaderParser(object):
295
309
  apos = fdecl.find("(", apos+1)
296
310
 
297
311
  fname = "cv." + fname.replace("::", ".")
298
- decl = [fname, rettype, [], []]
312
+ decl = [fname, rettype, [], [], None, docstring]
299
313
 
300
314
  # inline constructor implementation
301
315
  implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
@@ -362,11 +376,9 @@ class CppHeaderParser(object):
362
376
  decl[2].append("/A")
363
377
  if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
364
378
  decl[2].append("/C")
365
- if "virtual" in decl_str:
366
- print(decl_str)
367
379
  return decl
368
380
 
369
- def parse_func_decl(self, decl_str):
381
+ def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
370
382
  """
371
383
  Parses the function or method declaration in the form:
372
384
  [([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
@@ -375,12 +387,11 @@ class CppHeaderParser(object):
375
387
  [const] {; | <function_body>}
376
388
 
377
389
  Returns the function declaration entry:
378
- [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above)
390
+ [<func name>, <return value C-type>, <list of modifiers>, <list of arguments>, <original return type>, <docstring>] (see above)
379
391
  """
380
392
 
381
393
  if self.wrap_mode:
382
- if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
383
- ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
394
+ if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or ("CV_WRAP" in decl_str)):
384
395
  return []
385
396
 
386
397
  # ignore old API in the documentation check (for now)
@@ -400,12 +411,37 @@ class CppHeaderParser(object):
400
411
  arg, npos3 = self.get_macro_arg(decl_str, npos)
401
412
  func_modlist.append("="+arg)
402
413
  decl_str = decl_str[:npos] + decl_str[npos3+1:]
414
+ npos = decl_str.find("CV_WRAP_PHANTOM")
415
+ if npos >= 0:
416
+ decl_str, _ = self.get_macro_arg(decl_str, npos)
417
+ func_modlist.append("/phantom")
418
+ npos = decl_str.find("CV_WRAP_MAPPABLE")
419
+ if npos >= 0:
420
+ mappable, npos3 = self.get_macro_arg(decl_str, npos)
421
+ func_modlist.append("/mappable="+mappable)
422
+ classname = top[1]
423
+ return ['.'.join([classname, classname]), None, func_modlist, [], None, None]
424
+
425
+ virtual_method = False
426
+ pure_virtual_method = False
427
+ const_method = False
403
428
 
404
429
  # filter off some common prefixes, which are meaningless for Python wrappers.
405
430
  # note that we do not strip "static" prefix, which does matter;
406
431
  # it means class methods, not instance methods
407
- decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\
408
- ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")]).strip()
432
+ decl_str = self.batch_replace(decl_str, [("static inline", ""), ("inline", ""),\
433
+ ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", ""),
434
+ ("CV_DEPRECATED", ""), ("CV_DEPRECATED_EXTERNAL", "")]).strip()
435
+
436
+
437
+ if decl_str.strip().startswith('virtual'):
438
+ virtual_method = True
439
+
440
+ decl_str = decl_str.replace('virtual' , '')
441
+
442
+ end_tokens = decl_str[decl_str.rfind(')'):].split()
443
+ const_method = 'const' in end_tokens
444
+ pure_virtual_method = '=' in end_tokens and '0' in end_tokens
409
445
 
410
446
  static_method = False
411
447
  context = top[0]
@@ -443,6 +479,12 @@ class CppHeaderParser(object):
443
479
 
444
480
  rettype, funcname, modlist, argno = self.parse_arg(decl_start, -1)
445
481
 
482
+ # determine original return type, hack for return types with underscore
483
+ original_type = None
484
+ i = decl_start.rfind(funcname)
485
+ if i > 0:
486
+ original_type = decl_start[:i].replace("&", "").replace("const", "").strip()
487
+
446
488
  if argno >= 0:
447
489
  classname = top[1]
448
490
  if rettype == classname or rettype == "~" + classname:
@@ -474,7 +516,7 @@ class CppHeaderParser(object):
474
516
  funcname = self.get_dotted_name(funcname)
475
517
 
476
518
  if not self.wrap_mode:
477
- decl = self.parse_func_decl_no_wrap(decl_str, static_method)
519
+ decl = self.parse_func_decl_no_wrap(decl_str, static_method, docstring)
478
520
  decl[0] = funcname
479
521
  return decl
480
522
 
@@ -492,8 +534,6 @@ class CppHeaderParser(object):
492
534
  t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
493
535
  if not t:
494
536
  print("Error: no closing ')' at %d" % (self.lineno,))
495
- print(decl_str)
496
- print(decl_str[arg_start:])
497
537
  sys.exit(-1)
498
538
  if t == "<":
499
539
  angle_balance += 1
@@ -529,41 +569,46 @@ class CppHeaderParser(object):
529
569
  a = a[:eqpos].strip()
530
570
  arg_type, arg_name, modlist, argno = self.parse_arg(a, argno)
531
571
  if self.wrap_mode:
572
+ # TODO: Vectors should contain UMat, but this is not very easy to support and not very needed
573
+ vector_mat = "vector_{}".format("Mat")
574
+ vector_mat_template = "vector<{}>".format("Mat")
575
+
532
576
  if arg_type == "InputArray":
533
- arg_type = "Mat"
577
+ arg_type = mat
534
578
  elif arg_type == "InputOutputArray":
535
- arg_type = "Mat"
579
+ arg_type = mat
536
580
  modlist.append("/IO")
537
581
  elif arg_type == "OutputArray":
538
- arg_type = "Mat"
582
+ arg_type = mat
539
583
  modlist.append("/O")
540
584
  elif arg_type == "InputArrayOfArrays":
541
- arg_type = "vector_Mat"
585
+ arg_type = vector_mat
542
586
  elif arg_type == "InputOutputArrayOfArrays":
543
- arg_type = "vector_Mat"
587
+ arg_type = vector_mat
544
588
  modlist.append("/IO")
545
589
  elif arg_type == "OutputArrayOfArrays":
546
- arg_type = "vector_Mat"
590
+ arg_type = vector_mat
547
591
  modlist.append("/O")
548
- defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector<Mat>"),
549
- ("InputOutputArrayOfArrays", "vector<Mat>"),
550
- ("OutputArrayOfArrays", "vector<Mat>"),
551
- ("InputArray", "Mat"),
552
- ("InputOutputArray", "Mat"),
553
- ("OutputArray", "Mat"),
592
+ defval = self.batch_replace(defval, [("InputArrayOfArrays", vector_mat_template),
593
+ ("InputOutputArrayOfArrays", vector_mat_template),
594
+ ("OutputArrayOfArrays", vector_mat_template),
595
+ ("InputArray", mat),
596
+ ("InputOutputArray", mat),
597
+ ("OutputArray", mat),
554
598
  ("noArray", arg_type)]).strip()
555
599
  args.append([arg_type, arg_name, defval, modlist])
556
600
  npos = arg_start-1
557
601
 
558
- npos = decl_str.replace(" ", "").find("=0", npos)
559
- if npos >= 0:
560
- # skip pure virtual functions
561
- return []
562
-
563
602
  if static_method:
564
603
  func_modlist.append("/S")
604
+ if const_method:
605
+ func_modlist.append("/C")
606
+ if virtual_method:
607
+ func_modlist.append("/V")
608
+ if pure_virtual_method:
609
+ func_modlist.append("/PV")
565
610
 
566
- return [funcname, rettype, func_modlist, args]
611
+ return [funcname, rettype, func_modlist, args, original_type, docstring]
567
612
 
568
613
  def get_dotted_name(self, name):
569
614
  """
@@ -588,8 +633,10 @@ class CppHeaderParser(object):
588
633
  block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME]
589
634
  if block_type in ["file", "enum"]:
590
635
  continue
591
- if block_type not in ["struct", "class", "namespace"]:
592
- print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack))
636
+ if block_type in ["enum struct", "enum class"] and block_name == name:
637
+ continue
638
+ if block_type not in ["struct", "class", "namespace", "enum struct", "enum class"]:
639
+ print("Error at %d: there are non-valid entries in the current block stack %s" % (self.lineno, self.block_stack))
593
640
  sys.exit(-1)
594
641
  if block_name and (block_type == "namespace" or not qualified_name):
595
642
  n += block_name + "."
@@ -598,7 +645,7 @@ class CppHeaderParser(object):
598
645
  n = "cv.Algorithm"
599
646
  return n
600
647
 
601
- def parse_stmt(self, stmt, end_token):
648
+ def parse_stmt(self, stmt, end_token, mat="Mat", docstring=""):
602
649
  """
603
650
  parses the statement (ending with ';' or '}') or a block head (ending with '{')
604
651
 
@@ -645,7 +692,7 @@ class CppHeaderParser(object):
645
692
  exit(1)
646
693
  if classname.startswith("_Ipl"):
647
694
  classname = classname[1:]
648
- decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
695
+ decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, [], None, docstring]
649
696
  if bases:
650
697
  decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
651
698
  return stmt_type, classname, True, decl
@@ -660,25 +707,24 @@ class CppHeaderParser(object):
660
707
  exit(1)
661
708
  decl = []
662
709
  if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" in stmt)):
663
- decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
710
+ decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, [], None, docstring]
664
711
  if bases:
665
712
  decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
666
713
  return stmt_type, classname, True, decl
667
714
 
668
- if stmt.startswith("enum"):
669
- return "enum", "", True, None
670
-
671
- if stmt.startswith("namespace"):
672
- stmt_list = stmt.split()
715
+ if stmt.startswith("enum") or stmt.startswith("namespace"):
716
+ stmt_list = stmt.rsplit(" ", 1)
673
717
  if len(stmt_list) < 2:
674
718
  stmt_list.append("<unnamed>")
675
719
  return stmt_list[0], stmt_list[1], True, None
720
+
676
721
  if stmt.startswith("extern") and "\"C\"" in stmt:
677
722
  return "namespace", "", True, None
678
723
 
679
- if end_token == "}" and context == "enum":
724
+ if end_token == "}" and context.startswith("enum"):
680
725
  decl = self.parse_enum(stmt)
681
- return "enum", "", False, decl
726
+ name = stack_top[self.BLOCK_NAME]
727
+ return context, name, False, decl
682
728
 
683
729
  if end_token == ";" and stmt.startswith("typedef"):
684
730
  # TODO: handle typedef's more intelligently
@@ -690,7 +736,7 @@ class CppHeaderParser(object):
690
736
  # since we filtered off the other places where '(' can normally occur:
691
737
  # - code blocks
692
738
  # - function pointer typedef's
693
- decl = self.parse_func_decl(stmt)
739
+ decl = self.parse_func_decl(stmt, mat=mat, docstring=docstring)
694
740
  # we return parse_flag == False to prevent the parser to look inside function/method bodies
695
741
  # (except for tracking the nested blocks)
696
742
  return stmt_type, "", False, decl
@@ -717,7 +763,7 @@ class CppHeaderParser(object):
717
763
  def find_next_token(self, s, tlist, p=0):
718
764
  """
719
765
  Finds the next token from the 'tlist' in the input 's', starting from position 'p'.
720
- Returns the first occured token and its position, or ("", len(s)) when no token is found
766
+ Returns the first occurred token and its position, or ("", len(s)) when no token is found
721
767
  """
722
768
  token = ""
723
769
  tpos = len(s)
@@ -737,7 +783,7 @@ class CppHeaderParser(object):
737
783
  """
738
784
  self.hname = hname
739
785
  decls = []
740
- f = open(hname, "rt")
786
+ f = io.open(hname, 'rt', encoding='utf-8')
741
787
  linelist = list(f.readlines())
742
788
  f.close()
743
789
 
@@ -745,17 +791,19 @@ class CppHeaderParser(object):
745
791
  SCAN = 0 # outside of a comment or preprocessor directive
746
792
  COMMENT = 1 # inside a multi-line comment
747
793
  DIRECTIVE = 2 # inside a multi-line preprocessor directive
794
+ DOCSTRING = 3 # inside a multi-line docstring
748
795
 
749
796
  state = SCAN
750
797
 
751
798
  self.block_stack = [["file", hname, True, True, None]]
752
799
  block_head = ""
800
+ docstring = ""
753
801
  self.lineno = 0
754
802
  self.wrap_mode = wmode
755
803
 
756
804
  for l0 in linelist:
757
805
  self.lineno += 1
758
- #print self.lineno
806
+ #print(state, self.lineno, l0)
759
807
 
760
808
  l = l0.strip()
761
809
 
@@ -775,8 +823,22 @@ class CppHeaderParser(object):
775
823
  l = l[pos+2:]
776
824
  state = SCAN
777
825
 
826
+ if state == DOCSTRING:
827
+ pos = l.find("*/")
828
+ if pos < 0:
829
+ docstring += l + "\n"
830
+ continue
831
+ docstring += l[:pos] + "\n"
832
+ l = l[pos+2:]
833
+ state = SCAN
834
+
835
+ if l.startswith('CV__') or l.startswith('__CV_'): # just ignore these lines
836
+ #print('IGNORE: ' + l)
837
+ state = SCAN
838
+ continue
839
+
778
840
  if state != SCAN:
779
- print("Error at %d: invlid state = %d" % (self.lineno, state))
841
+ print("Error at %d: invalid state = %d" % (self.lineno, state))
780
842
  sys.exit(-1)
781
843
 
782
844
  while 1:
@@ -784,19 +846,34 @@ class CppHeaderParser(object):
784
846
 
785
847
  if not token:
786
848
  block_head += " " + l
787
- break
849
+ block_head = block_head.strip()
850
+ if len(block_head) > 0 and block_head[-1] == ')' and block_head.startswith('CV_ENUM_FLAGS('):
851
+ l = ''
852
+ token = ';'
853
+ else:
854
+ break
788
855
 
789
856
  if token == "//":
790
857
  block_head += " " + l[:pos]
791
- break
858
+ l = ''
859
+ continue
792
860
 
793
861
  if token == "/*":
794
862
  block_head += " " + l[:pos]
795
- pos = l.find("*/", pos+2)
796
- if pos < 0:
863
+ end_pos = l.find("*/", pos+2)
864
+ if len(l) > pos + 2 and l[pos+2] == "*":
865
+ # '/**', it's a docstring
866
+ if end_pos < 0:
867
+ state = DOCSTRING
868
+ docstring = l[pos+3:] + "\n"
869
+ break
870
+ else:
871
+ docstring = l[pos+3:end_pos]
872
+
873
+ elif end_pos < 0:
797
874
  state = COMMENT
798
875
  break
799
- l = l[pos+2:]
876
+ l = l[end_pos+2:]
800
877
  continue
801
878
 
802
879
  if token == "\"":
@@ -816,6 +893,7 @@ class CppHeaderParser(object):
816
893
 
817
894
  stmt = (block_head + " " + l[:pos]).strip()
818
895
  stmt = " ".join(stmt.split()) # normalize the statement
896
+ #print(stmt)
819
897
  stack_top = self.block_stack[-1]
820
898
 
821
899
  if stmt.startswith("@"):
@@ -826,13 +904,36 @@ class CppHeaderParser(object):
826
904
  if stack_top[self.PROCESS_FLAG]:
827
905
  # even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
828
906
  # since it can start with "public:"
829
- stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token)
907
+ docstring = docstring.strip()
908
+ stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring)
830
909
  if decl:
831
- if stmt_type == "enum":
832
- for d in decl:
833
- decls.append(d)
910
+ if stmt_type.startswith("enum"):
911
+ decls.append([stmt_type + " " + self.get_dotted_name(name), "", [], decl, None, ""])
834
912
  else:
835
913
  decls.append(decl)
914
+
915
+ if self._generate_gpumat_decls and "cv.cuda." in decl[0]:
916
+ # If function takes as one of arguments Mat or vector<Mat> - we want to create the
917
+ # same declaration working with GpuMat (this is important for T-Api access)
918
+ args = decl[3]
919
+ has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
920
+ if has_mat:
921
+ _, _, _, gpumat_decl = self.parse_stmt(stmt, token, mat="cuda::GpuMat", docstring=docstring)
922
+ decls.append(gpumat_decl)
923
+
924
+ if self._generate_umat_decls:
925
+ # If function takes as one of arguments Mat or vector<Mat> - we want to create the
926
+ # same declaration working with UMat (this is important for T-Api access)
927
+ args = decl[3]
928
+ has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
929
+ if has_mat:
930
+ _, _, _, umat_decl = self.parse_stmt(stmt, token, mat="UMat", docstring=docstring)
931
+ decls.append(umat_decl)
932
+
933
+ docstring = ""
934
+ if stmt_type == "namespace":
935
+ chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name]
936
+ self.namespaces.add('.'.join(chunks))
836
937
  else:
837
938
  stmt_type, name, parse_flag = "block", "", False
838
939
 
@@ -861,6 +962,8 @@ class CppHeaderParser(object):
861
962
  """
862
963
  for d in decls:
863
964
  print(d[0], d[1], ";".join(d[2]))
965
+ # Uncomment below line to see docstrings
966
+ # print('"""\n' + d[5] + '\n"""')
864
967
  for a in d[3]:
865
968
  print(" ", a[0], a[1], a[2], end="")
866
969
  if a[3]:
@@ -869,7 +972,7 @@ class CppHeaderParser(object):
869
972
  print()
870
973
 
871
974
  if __name__ == '__main__':
872
- parser = CppHeaderParser()
975
+ parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=False)
873
976
  decls = []
874
977
  # for hname in opencv_hdr_list:
875
978
  # decls += parser.parse(hname)