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.
- checksums.yaml +5 -5
- data/lib/rbind/core/rbase.rb +1 -1
- data/lib/rbind/core/rcast_operation.rb +1 -0
- data/lib/rbind/core/rclass.rb +41 -32
- data/lib/rbind/core/rgetter.rb +1 -1
- data/lib/rbind/core/rnamespace.rb +44 -10
- data/lib/rbind/core/roperation.rb +6 -0
- data/lib/rbind/core/rparameter.rb +5 -0
- data/lib/rbind/core/rtype_annotation.rb +1 -1
- data/lib/rbind/default_parser.rb +87 -8
- data/lib/rbind/generator_c.rb +19 -10
- data/lib/rbind/generator_extern.rb +3 -1
- data/lib/rbind/generator_ruby.rb +40 -13
- data/lib/rbind/templates/c/type_conversion.cc +9 -3
- data/lib/rbind/templates/c/type_conversion.hpp +2 -2
- data/lib/rbind/tools/hdr_parser.py +167 -64
- data/lib/rbind/types/std_map.rb +1 -2
- data/lib/rbind/types/std_vector.rb +1 -1
- data/manifest.xml +2 -0
- data/rbind.gemspec +4 -4
- metadata +13 -9
@@ -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?
|
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
|
|
data/lib/rbind/generator_ruby.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
|
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
|
-
|
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*[,:]
|
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 =
|
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, [("
|
408
|
-
("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("CV_INLINE", "")
|
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 =
|
577
|
+
arg_type = mat
|
534
578
|
elif arg_type == "InputOutputArray":
|
535
|
-
arg_type =
|
579
|
+
arg_type = mat
|
536
580
|
modlist.append("/IO")
|
537
581
|
elif arg_type == "OutputArray":
|
538
|
-
arg_type =
|
582
|
+
arg_type = mat
|
539
583
|
modlist.append("/O")
|
540
584
|
elif arg_type == "InputArrayOfArrays":
|
541
|
-
arg_type =
|
585
|
+
arg_type = vector_mat
|
542
586
|
elif arg_type == "InputOutputArrayOfArrays":
|
543
|
-
arg_type =
|
587
|
+
arg_type = vector_mat
|
544
588
|
modlist.append("/IO")
|
545
589
|
elif arg_type == "OutputArrayOfArrays":
|
546
|
-
arg_type =
|
590
|
+
arg_type = vector_mat
|
547
591
|
modlist.append("/O")
|
548
|
-
defval = self.batch_replace(defval, [("InputArrayOfArrays",
|
549
|
-
("InputOutputArrayOfArrays",
|
550
|
-
("OutputArrayOfArrays",
|
551
|
-
("InputArray",
|
552
|
-
("InputOutputArray",
|
553
|
-
("OutputArray",
|
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
|
592
|
-
|
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
|
-
|
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
|
724
|
+
if end_token == "}" and context.startswith("enum"):
|
680
725
|
decl = self.parse_enum(stmt)
|
681
|
-
|
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
|
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,
|
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:
|
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
|
-
|
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
|
-
|
858
|
+
l = ''
|
859
|
+
continue
|
792
860
|
|
793
861
|
if token == "/*":
|
794
862
|
block_head += " " + l[:pos]
|
795
|
-
|
796
|
-
if pos
|
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[
|
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
|
-
|
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
|
832
|
-
|
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)
|