mirror of https://github.com/ecmwf/eccodes.git
Refactoring to detect forward declarations
This commit is contained in:
parent
10077ae7e1
commit
527a289e66
|
@ -0,0 +1,125 @@
|
|||
import debug
|
||||
import arg_conv
|
||||
import func
|
||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||||
|
||||
common_includes = [
|
||||
"AccessorFactory.h",
|
||||
"AccessorUtils/ConversionHelper.h",
|
||||
"AccessorUtils/GribUtils.h",
|
||||
"AccessorUtils/GribAccessorFlag.h",
|
||||
"AccessorUtils/AccessorException.h"
|
||||
]
|
||||
|
||||
# Represents the converted C++ code for an Accessor
|
||||
class AccessorData:
|
||||
def __init__(self, name, super, factory_name):
|
||||
self._name = name
|
||||
self._super = super
|
||||
self._factory_name = factory_name
|
||||
self._members = []
|
||||
self._header_includes = []
|
||||
self._source_includes = []
|
||||
self._forward_declarations = []
|
||||
self._global_function = None
|
||||
self._inherited_methods = []
|
||||
self._private_methods = []
|
||||
self._static_functions = []
|
||||
self._constructor = None
|
||||
self._destructor = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def camel_case_name(self):
|
||||
debug.line("camel_case_name", f"name = {self._name}")
|
||||
return arg_conv.transform_variable_name(self._name)
|
||||
|
||||
@property
|
||||
def super(self):
|
||||
return self._super
|
||||
|
||||
@property
|
||||
def factory_name(self):
|
||||
return self._factory_name
|
||||
|
||||
@property
|
||||
def class_type(self):
|
||||
return "accessor"
|
||||
|
||||
@property
|
||||
def namespaces(self):
|
||||
return ["eccodes", self.class_type]
|
||||
|
||||
@property
|
||||
def nested_namespaces(self):
|
||||
return "::".join(self.namespaces)
|
||||
|
||||
@property
|
||||
def members(self):
|
||||
return self._members
|
||||
|
||||
@property
|
||||
def header_includes(self):
|
||||
return self._header_includes
|
||||
|
||||
@property
|
||||
def source_includes(self):
|
||||
return self._source_includes
|
||||
|
||||
@property
|
||||
def forward_declarations(self):
|
||||
return self._forward_declarations
|
||||
|
||||
@property
|
||||
def global_function(self):
|
||||
return self._global_function
|
||||
|
||||
@property
|
||||
def inherited_methods(self):
|
||||
return self._inherited_methods
|
||||
|
||||
@property
|
||||
def private_methods(self):
|
||||
return self._private_methods
|
||||
|
||||
@property
|
||||
def static_functions(self):
|
||||
return self._static_functions
|
||||
|
||||
@property
|
||||
def constructor(self):
|
||||
return self._constructor
|
||||
|
||||
@property
|
||||
def destructor(self):
|
||||
return self._destructor
|
||||
|
||||
@global_function.setter
|
||||
def global_function(self, value):
|
||||
if value:
|
||||
self._global_function = value
|
||||
|
||||
def add_header_include(self, entry):
|
||||
self._header_includes.append(entry)
|
||||
|
||||
def add_source_include(self, entry):
|
||||
self._source_includes.append(entry)
|
||||
|
||||
def add_forward_declaration(self, entry):
|
||||
if not entry in self._forward_declarations:
|
||||
self._forward_declarations.append(entry)
|
||||
|
||||
def add_member(self, member):
|
||||
self._members.append(member)
|
||||
|
||||
def add_inherited_method(self, func):
|
||||
self._inherited_methods.append(func)
|
||||
|
||||
def add_private_method(self, func):
|
||||
self._private_methods.append(func)
|
||||
|
||||
def add_static_function(self, func):
|
||||
self._static_functions.append(func)
|
|
@ -1,4 +1,4 @@
|
|||
from convert_debug import debug_line
|
||||
import debug
|
||||
import re
|
||||
|
||||
# Represent an argument in the form TYPE NAME
|
||||
|
@ -18,26 +18,41 @@ class Arg:
|
|||
return self.type == other.type and self.name == other.name
|
||||
return False
|
||||
|
||||
# Return the "raw" type, e.g. char for char* or int for const int&
|
||||
@property
|
||||
def underlying_type(self):
|
||||
m = re.match(r"(?:const)?\s*(\w+)\s*[\*&]?\*?", self.type)
|
||||
if m:
|
||||
return m.group(1)
|
||||
else:
|
||||
return self.type
|
||||
|
||||
# Create Arg from an input string
|
||||
@classmethod
|
||||
def from_string(cls, input):
|
||||
|
||||
# Note: "return x;" looks like a variable declaration, so we explicitly exclude this...
|
||||
# Note: We ignore const for now...
|
||||
m = re.match(r"(?:const)?\s*(\w+)\s*(\*)?\s*(\w+)\s*(\[\d*\])?", input)
|
||||
m = re.match(r"(const)?(struct)?\s*(\w+)\s*(\*+)?\s*(\w+)\s*(\[\d*\])?", input)
|
||||
|
||||
if m:
|
||||
arg_type = m.group(1)
|
||||
arg_type = ""
|
||||
if m.group(1):
|
||||
arg_type += m.group(1) + " "
|
||||
if m.group(2):
|
||||
arg_type += m.group(2) # Add * if present...
|
||||
arg_name = m.group(3)
|
||||
arg_type += m.group(2) + " "
|
||||
arg_type += m.group(3)
|
||||
if m.group(4):
|
||||
# Handle array declaration e.g. char buf[10]
|
||||
arg_type += m.group(4)
|
||||
arg_type += m.group(4) # Add * if present...
|
||||
|
||||
arg_name = m.group(5)
|
||||
if m.group(6):
|
||||
# Handle array declaration e.g. char buf[10]
|
||||
arg_type += m.group(6)
|
||||
|
||||
debug_line("Arg from_string", f"Creating Arg: {arg_type} {arg_name} from input: {input}")
|
||||
return cls(arg_type, arg_name)
|
||||
|
||||
debug_line("Arg from_string", f"Couldn't create arg from input: {input}")
|
||||
debug.line("Arg from_string", f"Couldn't create arg from input: {input}")
|
||||
return None
|
||||
|
||||
# Generate a string to represent the Arg's declaration
|
||||
|
@ -82,19 +97,3 @@ def arg_string(arg):
|
|||
return arg.as_declaration()
|
||||
|
||||
return "None"
|
||||
|
||||
def transform_variable_name(name):
|
||||
name_parts = name.split("_")
|
||||
|
||||
if len(name_parts) > 1:
|
||||
name = name_parts[0] + "".join(x.capitalize() for x in name_parts[1:])
|
||||
|
||||
return name[0].lower() + name[1:]
|
||||
|
||||
def transform_function_name(name):
|
||||
name = re.sub(rf"^[gs]et_", f"", name)
|
||||
return transform_variable_name(name)
|
||||
|
||||
def transform_class_name(name):
|
||||
name = transform_variable_name(name)
|
||||
return name[0].upper() + name[1:]
|
|
@ -0,0 +1,128 @@
|
|||
# Convert Arg from C to C++
|
||||
|
||||
import re
|
||||
import debug
|
||||
import arg
|
||||
|
||||
# These will be used if no other supplied...
|
||||
common_type_transforms = {
|
||||
"grib_accessor*" : None,
|
||||
"grib_handle*" : None,
|
||||
"grib_context*" : None,
|
||||
"char**" : "std::string&",
|
||||
"char*" : "std::string",
|
||||
"char[]" : "std::string",
|
||||
"grib_iarray*" : "std::vector<long>"
|
||||
}
|
||||
|
||||
class ArgConverter:
|
||||
def __init__(self, carg):
|
||||
self._carg = carg
|
||||
|
||||
# Returns the equivalent C++ arg (name and type), which could be None
|
||||
def to_cpp_arg(self, type_transforms=common_type_transforms):
|
||||
updated_carg = update_carg_format(self._carg)
|
||||
|
||||
# [1] Check for defined transforms
|
||||
for k, v in type_transforms.items():
|
||||
if k == updated_carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return arg.Arg(v, transform_variable_name(updated_carg.name))
|
||||
|
||||
# [2] Process any other array types
|
||||
m = re.match(r"(\w*)(\[\d*\])", updated_carg.type)
|
||||
if m:
|
||||
for k, v in type_transforms.items():
|
||||
if k == m.group(1):
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return arg.Arg(f"std::vector<{v}>", transform_variable_name(updated_carg.name))
|
||||
|
||||
return arg.Arg(f"std::vector<{m.group(1)}>", transform_variable_name(updated_carg.name))
|
||||
|
||||
# [3] Process other mapped types
|
||||
for k, v in type_transforms.items():
|
||||
if k == updated_carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return arg.Arg(v, transform_variable_name(updated_carg.name))
|
||||
|
||||
# [3] Return None for grib_accessor_*
|
||||
m = re.match(r"grib_accessor_", self._carg.type)
|
||||
if m:
|
||||
return None
|
||||
|
||||
# [4] Pointer types
|
||||
m = re.match(r"(\w*)(\*+)", self._carg.type)
|
||||
if m:
|
||||
cpptype = f"std::vector<{m.group(1)}>"
|
||||
if m.group(2) == "**":
|
||||
cpptype += "&"
|
||||
return arg.Arg(cpptype, transform_variable_name(updated_carg.name))
|
||||
|
||||
# [5] Everything else
|
||||
return arg.Arg(self._carg.type, transform_variable_name(self._carg.name))
|
||||
|
||||
# Returns the equivalent C++ arg (name and type), which could be None
|
||||
# The type is almost the same as for the function body, with the
|
||||
# following exceptions:
|
||||
# - pointers are converted to references (not std::vector)
|
||||
# - arrays are converted to std::vector references
|
||||
def to_cpp_func_sig_arg(self, type_transforms = common_type_transforms):
|
||||
|
||||
# [1] Pointer types
|
||||
m = re.match(r"(\w*)(\*+)", self._carg.type)
|
||||
if m: #self._carg.type[-1] == "*":
|
||||
# Check for defined transforms
|
||||
for k, v in type_transforms.items():
|
||||
if k == self._carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return arg.Arg(v+"&", transform_variable_name(self._carg.name))
|
||||
|
||||
# Other pointers: removing * to avoid getting std::vector type back (unless it's **)
|
||||
self._carg = arg.Arg(self._carg.type[:-1], self._carg.name)
|
||||
cpparg = self.to_cpp_arg(type_transforms)
|
||||
|
||||
if cpparg:
|
||||
cpparg.type += "&"
|
||||
|
||||
return cpparg
|
||||
|
||||
# [2] Everything else
|
||||
cpparg = self.to_cpp_arg(type_transforms)
|
||||
|
||||
if cpparg and self._carg.type[-1] == "]":
|
||||
cpparg.type += "&"
|
||||
|
||||
return cpparg
|
||||
|
||||
|
||||
# Tidy up type and arg: basically change int[10] val to int[] val
|
||||
def update_carg_format(carg):
|
||||
m = re.search(r"(\w*)(\[\d*\])", carg.type)
|
||||
if m:
|
||||
return arg.Arg(m.group(1)+"[]", carg.name)
|
||||
|
||||
return carg
|
||||
|
||||
# Convert C-style snake_case type name to C++-style PascalCase
|
||||
def transform_type_name(name):
|
||||
name_parts = name.split("_")
|
||||
|
||||
if len(name_parts) > 1:
|
||||
return "".join(x.capitalize() for x in name_parts)
|
||||
else:
|
||||
return name[0].upper() + name[1:]
|
||||
|
||||
# Change C-style snake_case variable name to C++-style camelCase
|
||||
def transform_variable_name(name):
|
||||
name = transform_type_name(name)
|
||||
|
||||
return name[0].lower() + name[1:]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
# Convert C definitions (e.g. to enums)
|
||||
|
||||
GribStatusConverter = {
|
||||
"GRIB_SUCCESS": "GribStatus::SUCCESS",
|
|
@ -1,7 +1,7 @@
|
|||
# Function transforms to apply to all converted C code
|
||||
# C code substitutions to apply
|
||||
|
||||
import re
|
||||
from convert_debug import debug_line
|
||||
import debug
|
||||
|
||||
c_lib_substitutions = {
|
||||
r"\bstrcmp\((.*),\s*(.*)\s*\)\s*([!=]=)\s*\d+": r"\1 \3 \2",
|
||||
|
@ -31,7 +31,7 @@ grib_iarray_substitutions = {
|
|||
r"^\s*(.*?\bgrib_iarray_delete)": r"// [Removed grib_iarray_delete] \1",
|
||||
}
|
||||
|
||||
def apply_all_func_transforms(line):
|
||||
def apply_all_substitutions(line):
|
||||
|
||||
func_substitutions = [
|
||||
c_lib_substitutions,
|
||||
|
@ -44,6 +44,6 @@ def apply_all_func_transforms(line):
|
|||
for k, v in func_substitution_dict.items():
|
||||
line, count = re.subn(k, v, line)
|
||||
if count:
|
||||
debug_line("apply_all_func_transforms", f"Updated line: {line}")
|
||||
debug.line("apply_all_substitutions", f"Updated line: {line}")
|
||||
|
||||
return line
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
from method import *
|
||||
|
||||
# Specialisation of AccessorData method
|
||||
class ConstructorMethod(Method):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
||||
# Return all arg names in a string, for example to use to call the base constructor
|
||||
@property
|
||||
def arg_name_string(self):
|
||||
return ", ".join([f"{a.name}" for a in self.args if a])
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
from method_conv import *
|
||||
import constructor_method
|
||||
|
||||
# Specialisation of MethodConverter for AccessorData Methods
|
||||
# From: init(grib_accessor* a, const long len, grib_arguments* arg)
|
||||
# To: CONSTRUCTOR(AccessorInitData const& initData)
|
||||
class ConstructorMethodConverter(MethodConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return constructor_method.ConstructorMethod(cppfuncsig)
|
||||
|
||||
def to_cpp_args(self, cfuncsig):
|
||||
return [None, None, arg.Arg("AccessorInitData const&", "initData")]
|
||||
|
||||
def process_variables_initial_pass(self, line):
|
||||
|
||||
# Transform the argument getters
|
||||
line = re.sub(rf"\blen\b", "initData.length", line)
|
||||
line = re.sub(rf"\bgrib_arguments_get_name\s*\(.*?,\s*\w+\s*,\s*(.*)?\)", rf"AccessorName(std::get<std::string>(initData.args[\1].second))", line)
|
||||
line = re.sub(rf"\bgrib_arguments_get_(\w+)\(.*?, arg, (\d+)\)", rf"std::get<\1>(initData.args[\2].second)", line)
|
||||
|
||||
return super().process_variables_initial_pass(line)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +0,0 @@
|
|||
|
||||
func_pad = 30
|
||||
debug_line_enabled = True
|
||||
debug_filter_include = ["finalise", "parse_file"]
|
||||
debug_filter_exclude = []
|
||||
|
||||
def debug_line(func, text):
|
||||
if not debug_line_enabled:
|
||||
return
|
||||
|
||||
if debug_filter_include and not func in debug_filter_include:
|
||||
return
|
||||
|
||||
if debug_filter_exclude and func in debug_filter_exclude:
|
||||
return
|
||||
|
||||
if len(func) > func_pad:
|
||||
print(f">>>>>")
|
||||
print(f">>>>> PADDING ({func_pad}) TOO SMALL FOR FUNC NAME: {func} - size={len(func)}")
|
||||
print(f">>>>>")
|
||||
|
||||
print(f"{func:{func_pad}}: {text}")
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
from datetime import datetime
|
||||
|
||||
func_pad = 30
|
||||
debug_enabled = True
|
||||
debug_filter_include = ["add_forward_declarations"]
|
||||
debug_filter_exclude = []
|
||||
|
||||
show_time = False
|
||||
start_time = datetime.now()
|
||||
|
||||
def enable_debug():
|
||||
global debug_enabled
|
||||
debug_enabled = True
|
||||
|
||||
def disable_debug():
|
||||
global debug_enabled
|
||||
debug_enabled = False
|
||||
|
||||
def reset_timer():
|
||||
global start_time
|
||||
start_time = datetime.now()
|
||||
|
||||
def line(func, text):
|
||||
if not debug_enabled:
|
||||
return
|
||||
|
||||
if debug_filter_include and not func in debug_filter_include:
|
||||
return
|
||||
|
||||
if debug_filter_exclude and func in debug_filter_exclude:
|
||||
return
|
||||
|
||||
if len(func) > func_pad:
|
||||
print(f">>>>>")
|
||||
print(f">>>>> PADDING ({func_pad}) TOO SMALL FOR FUNC NAME: {func} - size={len(func)}")
|
||||
print(f">>>>>")
|
||||
|
||||
global start_time
|
||||
elapsed_time_str = ""
|
||||
if show_time:
|
||||
current_time = datetime.now()
|
||||
elapsed_time = current_time - start_time
|
||||
elapsed_time_str = f",{elapsed_time.microseconds},"
|
||||
|
||||
# Multiline support
|
||||
if text[0] == "\n":
|
||||
print()
|
||||
text = text[1:]
|
||||
|
||||
lines = text.split("\n")
|
||||
|
||||
print(f"{func:{func_pad}}:{elapsed_time_str} {lines[0]}")
|
||||
|
||||
func = ""
|
||||
for line in lines[1:]:
|
||||
print(f"{func:{func_pad}} {line}")
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
from method import *
|
||||
|
||||
# Specialisation of AccessorData method
|
||||
class DestructorMethod(Method):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
from method_conv import *
|
||||
import destructor_method
|
||||
|
||||
# Specialisation of MethodConverter for AccessorData Methods
|
||||
class DestructorMethodConverter(MethodConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return destructor_method.DestructorMethod(cppfuncsig)
|
|
@ -0,0 +1,62 @@
|
|||
import debug
|
||||
import arg
|
||||
|
||||
global_function_name = "Global"
|
||||
|
||||
class Function:
|
||||
def __init__(self, func_sig) -> None:
|
||||
self._func_sig = func_sig
|
||||
self._lines = []
|
||||
|
||||
def is_empty(self):
|
||||
return len(self._lines) == 0
|
||||
|
||||
def clear_lines(self):
|
||||
self._lines.clear()
|
||||
|
||||
def add_line(self, line):
|
||||
self._lines.append(line)
|
||||
|
||||
@property
|
||||
def func_sig(self):
|
||||
return self._func_sig
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._func_sig.name
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self._func_sig.args
|
||||
|
||||
# Return all args in a string
|
||||
@property
|
||||
def arg_string(self):
|
||||
return ", ".join([f"{a.type} {a.name}" for a in self.args if a])
|
||||
|
||||
@property
|
||||
def return_type(self):
|
||||
return self._func_sig.return_type
|
||||
|
||||
@property
|
||||
def static(self):
|
||||
return self._func_sig.static
|
||||
|
||||
@property
|
||||
def const(self):
|
||||
return ""
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
return "" if self._func_sig._template is None else self._func_sig._template
|
||||
|
||||
@property
|
||||
def code(self):
|
||||
if self._lines:
|
||||
assert self._lines[0].strip() == "{", "\n".join(self._lines)
|
||||
assert self._lines[-1].strip() == "}", "\n".join(self._lines)
|
||||
return self._lines[1:-1]
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
return "\n".join(self._lines)
|
|
@ -0,0 +1,490 @@
|
|||
# Convert C function to C++
|
||||
import debug
|
||||
import func
|
||||
import funcsig
|
||||
import arg
|
||||
import arg_conv
|
||||
import c_def_conv
|
||||
import c_subs
|
||||
import re
|
||||
|
||||
# Change C-style snake_case function name to C++-style camelCase name
|
||||
# Also, remove get_ and set_ prefixes
|
||||
def transform_function_name(name):
|
||||
name = re.sub(rf"^[gs]et_", f"", name)
|
||||
return arg_conv.transform_variable_name(name)
|
||||
|
||||
# Generic function converter - overridden as required
|
||||
class FunctionConverter:
|
||||
def __init__(self):
|
||||
self._in_comment_block = False
|
||||
self._transforms = None
|
||||
self._cfunction = None # Set whilst processing...
|
||||
self._cppfunction = None # Set whilst processing...
|
||||
|
||||
@property
|
||||
def transforms(self):
|
||||
return self._transforms
|
||||
|
||||
def to_cpp_func_sig(self, cfuncsig):
|
||||
cppfuncsig = funcsig.FuncSig(
|
||||
self.to_cpp_return_type(cfuncsig),
|
||||
self.to_cpp_name(cfuncsig),
|
||||
self.to_cpp_args(cfuncsig),
|
||||
cfuncsig.template)
|
||||
cppfuncsig.static = cfuncsig.static
|
||||
|
||||
return cppfuncsig
|
||||
|
||||
def to_cpp_name(self, cfuncsig):
|
||||
return transform_function_name(cfuncsig.name)
|
||||
|
||||
def to_cpp_return_type(self, cfuncsig):
|
||||
# We'll assume int means GribStatus
|
||||
if cfuncsig.return_type == "int":
|
||||
return "GribStatus"
|
||||
else:
|
||||
return cfuncsig.return_type
|
||||
|
||||
# This should return the same number of cppargs as there are cargs (set unused cppargs to None)
|
||||
def to_cpp_args(self, cfuncsig):
|
||||
cppargs = []
|
||||
for entry in cfuncsig.args:
|
||||
arg_converter = arg_conv.ArgConverter(entry)
|
||||
cpparg = arg_converter.to_cpp_func_sig_arg()
|
||||
cppargs.append(cpparg)
|
||||
|
||||
return cppargs
|
||||
|
||||
# Convert cfunction to cppfunction
|
||||
# This is the main entry point and should not be overridden - override
|
||||
# the specific function calls within as required...
|
||||
def to_cpp_function(self, cfunction, transforms):
|
||||
self._transforms = transforms
|
||||
self._transforms.clear_local_args()
|
||||
|
||||
self._cfunction = cfunction
|
||||
cppfuncsig = self.to_cpp_func_sig(self._cfunction.func_sig)
|
||||
# Store the C to C++ function arg transforms
|
||||
for index, carg in enumerate(self._cfunction.func_sig.args):
|
||||
cpparg = cppfuncsig.args[index]
|
||||
self._transforms.add_local_args(carg, cpparg)
|
||||
|
||||
self._cppfunction = self.create_cpp_function(cppfuncsig)
|
||||
for line in self.create_cpp_body():
|
||||
self._cppfunction.add_line(line)
|
||||
|
||||
return self._cppfunction
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
assert False, "[WARNING] Unexpected call to FunctionConverter instance - consider overriding!"
|
||||
return func.Function(cppfuncsig)
|
||||
|
||||
def skip_line(self, line):
|
||||
if not line:
|
||||
debug.line("skip_line", f"[Empty]: {line}")
|
||||
return True
|
||||
|
||||
if re.match(r"^\s*//", line):
|
||||
debug.line("skip_line", f"[C++ Comment]: {line}")
|
||||
return True
|
||||
|
||||
m = re.match(r"^\s*(/\*)?.*?(\*/)?$", line)
|
||||
|
||||
if m and m.group(1):
|
||||
if m.group(2):
|
||||
self._in_comment_block = False
|
||||
debug.line("skip_line", f"[C Comment]: {line}")
|
||||
else:
|
||||
debug.line("skip_line", f"[C Comment Start]: {line}")
|
||||
self._in_comment_block = True
|
||||
return True
|
||||
|
||||
if m and m.group(2):
|
||||
self._in_comment_block = False
|
||||
debug.line("skip_line", f"[C Comment End ]: {line}")
|
||||
return True
|
||||
|
||||
if self._in_comment_block:
|
||||
debug.line("skip_line", f"[C Comment Block]: {line}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# Override this to provide any initial conversions before the main update_cpp_line runs
|
||||
def process_variables_initial_pass(self, line):
|
||||
return line
|
||||
|
||||
# Find type declarations and store in the transforms
|
||||
def process_type_declarations(self, line):
|
||||
|
||||
# struct declaration [1]: [typedef] struct S [S]
|
||||
m = re.match(r"^(typedef)?\s*struct\s+(\w+)\s*(\w*)?(;)?$", line)
|
||||
if m:
|
||||
if m.group(1) and m.group(4):
|
||||
line = "// [Removed struct typedef] " + line
|
||||
else:
|
||||
ctype = m.group(2)
|
||||
cpptype = arg_conv.transform_type_name(ctype)
|
||||
|
||||
self._transforms.add_to_types(ctype, cpptype)
|
||||
|
||||
line = re.sub(m.re, f"struct {cpptype}", line)
|
||||
debug.line("process_type_declarations", f"Added struct type transform: {ctype} -> {cpptype} [after ]: {line}")
|
||||
|
||||
carg = arg.Arg("struct", ctype)
|
||||
cpparg = arg.Arg("struct", cpptype)
|
||||
self._transforms.add_local_args(carg, cpparg)
|
||||
debug.line("process_type_declarations", f"Adding struct to local arg map: {carg.type} {carg.name} -> {cpparg.type} {cpparg.name} [after ]: {line}")
|
||||
|
||||
# struct declaration [2]: } S
|
||||
m = re.match(r"^}\s*(\w+)", line)
|
||||
if m and m.group(1) not in ["else"]:
|
||||
# We'll assume this definition is covered by [1]
|
||||
line = re.sub(m.re, "}", line)
|
||||
debug.line("process_type_declarations", f"Removed extraneous struct definition: {m.group(1)} [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Update any well know return values
|
||||
def process_return_variables(self, line):
|
||||
ret = "GribStatus"
|
||||
if self._cppfunction.return_type == ret:
|
||||
for ret_var in ["err", "ret"]:
|
||||
# [1] Assigning to function result - we assume the function returns the correct type!
|
||||
m = re.search(rf"\bint\s+{ret_var}\s*=\s*(.*)\(", line)
|
||||
if m:
|
||||
line = re.sub(m.re, rf"{ret} {ret_var} = {m.group(1)}(", line)
|
||||
debug.line("process_return_variables", f"return value assigned via function [after ]: {line}")
|
||||
|
||||
m = re.search(rf"\bint\b(\s+{ret_var}\s+=\s*)(\d+)[,;]", line)
|
||||
if m:
|
||||
line = re.sub(m.re, rf"{ret}\1{ret}{{\2}};", line)
|
||||
debug.line("process_return_variables", f"return value assigned to value [after ]: {line}")
|
||||
|
||||
m = re.search(rf"(\(\s*{ret_var}\s*)\)", line)
|
||||
if m:
|
||||
line = re.sub(m.re, rf"\1 != {ret}::SUCCESS)", line)
|
||||
debug.line("process_return_variables", f"return value comparison updated [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Find variable declarations with assignments, e.g. char* buf = "Data"; and then:
|
||||
# 1. Update the type if required / delete the line if no longer valid
|
||||
# 2. Store in the arg_map for future reference
|
||||
def process_variable_declarations(self, line):
|
||||
|
||||
# Note: "return x;" looks like a variable declaration, so we explicitly exclude it
|
||||
# static and const are ignored
|
||||
prefix = r"^\s*(?!return)(?:static)?(?:const)?"
|
||||
m = re.match(rf"{prefix}\s*((\w+\*?\*?)\s+(\w+)\s*(\[\d*\])?)\s*[=;]", line)
|
||||
|
||||
if m:
|
||||
carg = arg.Arg.from_string(m.group(1))
|
||||
debug.line("process_variable_declarations", f"MATCH: {m.group(0)} : {line}")
|
||||
|
||||
if m and carg:
|
||||
arg_converter = arg_conv.ArgConverter(carg)
|
||||
cpparg = arg_converter.to_cpp_arg(self._transforms.types)
|
||||
|
||||
if not cpparg:
|
||||
debug.line("process_variable_declarations", f"Found var declaration to delete: {carg.type} {carg.name}")
|
||||
self._transforms.add_local_args(carg, None)
|
||||
debug.line("process_variable_declarations", f"--> deleting: {line}")
|
||||
return ""
|
||||
else:
|
||||
debug.line("process_variable_declarations", f"Found var declaration to store: {carg.type} {carg.name} -> {cpparg.type} {cpparg.name}")
|
||||
self._transforms.add_local_args(carg, cpparg)
|
||||
if carg.type != cpparg.type or carg.name != cpparg.name:
|
||||
line = re.sub(rf"{re.escape(m.group(1))}", f"{cpparg.as_declaration()}", line)
|
||||
debug.line("process_variable_declarations", f"Transformed line: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Remove any variables that have been marked for deletion
|
||||
def process_deleted_variables(self, line):
|
||||
# Find any deleted variables that are being assigned to, and delete the line
|
||||
m = re.match(r"^\s*\**(\w+)\s*=", line)
|
||||
if m:
|
||||
debug.line("process_deleted_variables", f"Found var assignment: var={m.group(1)}: {line}")
|
||||
for carg, cpparg in self._transforms.all_args.items():
|
||||
if carg.name == m.group(1) and not cpparg:
|
||||
debug.line("process_deleted_variables", f"Var assignment marked for delete, var={m.group(1)} - deleting: {line}")
|
||||
line = f"// [{m.group(1)} removed] " + line
|
||||
return line
|
||||
|
||||
# Remove any deleted vars that remain (i.e. as an argument to a function call)
|
||||
for carg, cpparg in self._transforms.all_args.items():
|
||||
if not cpparg and re.match(rf"^.*\b{carg.name}\b\s*,*", line):
|
||||
line = re.sub(rf"[&\*]?\b{carg.name}(->)?\b\s*,*", "", line)
|
||||
debug.line("process_deleted_variables", f"Removing arg={carg.name} [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def apply_variable_transforms(self, line):
|
||||
# Assume sizeof(x)/sizeof(*x) or sizeof(x)/sizeof(x[0]) refers to a container with a size() member...
|
||||
m = re.search(r"\bsizeof\((.*?)\)\s*/\s*sizeof\(\s*(?:\*)?\1(?:\[0\])?\s*\)", line)
|
||||
if m:
|
||||
line = re.sub(m.re, f"{m.group(1)}.size()", line)
|
||||
debug.line("apply_variable_transforms", f"sizeof transform [after ]: {line}")
|
||||
|
||||
# See if sizeof(x) needs to be replaced by x.size()
|
||||
m = re.search(r"\bsizeof\((.*?)\)", line)
|
||||
if m:
|
||||
for _, cpparg in self._transforms.all_args.items():
|
||||
if cpparg and cpparg.name == m.group(1):
|
||||
if arg.is_container(cpparg):
|
||||
line = re.sub(m.re, f"{m.group(1)}.size()", line)
|
||||
debug.line("apply_variable_transforms", f"sizeof(x) transform for container [after ]: {line}")
|
||||
break
|
||||
|
||||
return line
|
||||
|
||||
# Special handling for function pointers: [typedef] RET (*PFUNC)(ARG1, ARG2, ..., ARGN);
|
||||
def process_function_pointers(self, line):
|
||||
|
||||
m = re.match(r"^(?:typedef)\s*(\w+\**)\s*\(\s*\*(\s*\w+)\s*\)\s*\((.*)\)", line)
|
||||
if m:
|
||||
carg = arg.Arg(m.group(1), m.group(2))
|
||||
arg_converter = arg_conv.ArgConverter(carg)
|
||||
cpparg = arg_converter.to_cpp_arg(self._transforms.types)
|
||||
|
||||
# Assume functions returning int will now return GribStatus
|
||||
if cpparg.type == "int":
|
||||
cpparg.type = "GribStatus"
|
||||
|
||||
debug.line("process_function_pointers", f"Adding var to local arg map: {carg.type} {carg.name} -> {cpparg.type} {cpparg.name} [after ]: {line}")
|
||||
self._transforms.add_local_args(carg, cpparg)
|
||||
|
||||
# Parse the function arg types
|
||||
cpp_arg_types = []
|
||||
for arg_type in [a.strip() for a in m.group(3).split(",")]:
|
||||
arg_converter = arg_conv.ArgConverter(arg.Arg(arg_type, "Dummy"))
|
||||
cpp_sig_arg = arg_converter.to_cpp_func_sig_arg()
|
||||
if cpp_sig_arg:
|
||||
cpp_arg_types.append(cpp_sig_arg.type)
|
||||
|
||||
# Apply the transform
|
||||
line = re.sub(rf"(\w+\**)\s*\(\s*\*(\s*\w+)\s*\)\s*\((.*)\)", f"{cpparg.type}(*{cpparg.name})({','.join([a for a in cpp_arg_types])})", line)
|
||||
debug.line("process_function_pointers", f"Transformed line: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def process_remaining_cargs(self, line):
|
||||
|
||||
# Update any C arg that remain (i.e. as an argument to a function call)
|
||||
# This will also remove any unnecessary pointers/refs
|
||||
# Note: We ignore anything in quotes!
|
||||
for carg, cpparg in self._transforms.all_args.items():
|
||||
debug.line("process_remaining_cargs", f"carg={arg.arg_string(carg)} cpparg={arg.arg_string(cpparg)}")
|
||||
|
||||
if not cpparg:
|
||||
continue
|
||||
|
||||
# Note: We assume *var and &var should be replaced with var
|
||||
m = re.search(rf"(?<!\")[&\*]?\b{carg.name}\b(\s*,*)(?!\")", line)
|
||||
|
||||
if m and m.group(0) != cpparg.name:
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{cpparg.name}{m.group(1)}", line)
|
||||
debug.line("process_remaining_cargs", f"Substituted \"{m.group(0)}\" with \"{cpparg.name}{m.group(1)}\" [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Update any references to global args
|
||||
def process_global_cargs(self, line):
|
||||
# Update any global C args used (i.e. as an argument to a function call)
|
||||
# This will also remove any unnecessary pointers/refs
|
||||
# Note: We ignore anything in quotes!
|
||||
for carg, cpparg in self._transforms.all_args.items():
|
||||
if not cpparg:
|
||||
continue
|
||||
|
||||
# Note: We assume *var and &var should be replaced with var
|
||||
m = re.search(rf"(?<!\")[&\*]?\b{carg.name}\b(\s*,*)(?!\")", line)
|
||||
|
||||
if m and m.group(0) != cpparg.name:
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{cpparg.name}{m.group(1)}", line)
|
||||
debug.line("process_global_cargs", f"Substituted \"{m.group(0)}\" with \"{cpparg.name}\"{m.group(1)}\" [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def convert_grib_values(self, line):
|
||||
for k, v in c_def_conv.GribStatusConverter.items():
|
||||
line, count = re.subn(rf"{k}", rf"{v}", line)
|
||||
if count:
|
||||
debug.line("convert_grib_values", f"Replaced {k} with {v} [after ]: {line}")
|
||||
|
||||
for k, v in c_def_conv.GribTypeConverter.items():
|
||||
line, count = re.subn(rf"{k}", rf"{v}", line)
|
||||
if count:
|
||||
debug.line("convert_grib_values", f"Replaced {k} with {v} [after ]: {line}")
|
||||
|
||||
for k, v in c_def_conv.GribAccessorFlagConverter.items():
|
||||
line, count = re.subn(rf"{k}", rf"toInt({v})", line)
|
||||
if count:
|
||||
debug.line("convert_grib_values", f"Replaced {k} with {v} [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def convert_grib_utils(self, line):
|
||||
for util in c_def_conv.GribUtilFuncs:
|
||||
m = re.search(rf"\b{util}\b", line)
|
||||
if m:
|
||||
cpp_util = transform_function_name(util)
|
||||
line = re.sub(m.re, f"{cpp_util}", line)
|
||||
debug.line("convert_grib_utils", f"Replaced {util} with {cpp_util} [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def apply_get_set_substitutions(self, line):
|
||||
# [1] grib_[gs]et_TYPE[_array][_internal](...) -> unpackTYPE(...)
|
||||
# Note: This regex is complicated (!) by calls like grib_get_double(h, lonstr, &(lon[i]));
|
||||
# The (?:&)?([\(\w\[\]\)]*)? section is added to match this and strip off the & (and it appears twice!)
|
||||
m = re.search(r"\bgrib_([gs]et)_(\w+?)(?:_array)?(?:_internal)?\(\s*(h\s*,)?\s*(\"?\w*\"?)\s*,?\s*(?:&)?([\(\w\[\]\)]*)?\s*,?\s*(?:&)?([\(\w\[\]\)]*)?\s*\)", line)
|
||||
if m:
|
||||
accessor_name = m.group(4)
|
||||
if accessor_name[0] == "\"":
|
||||
accessor_name = "AccessorName(" + accessor_name + ")"
|
||||
else:
|
||||
for k,v in self._transforms.all_args.items():
|
||||
if v and v.name == accessor_name and v.type == "std::string":
|
||||
accessor_name = "AccessorName(" + accessor_name + ")"
|
||||
|
||||
if m.group(1) == "get":
|
||||
if m.group(2) == "size":
|
||||
line = re.sub(m.re, f"getSizeHelper({accessor_name}, {m.group(5)})", line)
|
||||
else:
|
||||
line = re.sub(m.re, f"unpack{m.group(2).capitalize()}Helper({accessor_name}, {m.group(5)})", line)
|
||||
else:
|
||||
line = re.sub(m.re, f"pack{m.group(2).capitalize()}Helper({accessor_name}, {m.group(5)})", line)
|
||||
|
||||
debug.line("apply_get_set_substitutions", f"Result of substitution: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def apply_function_transforms(self, line):
|
||||
line = c_subs.apply_all_substitutions(line)
|
||||
|
||||
# Static function substitutions
|
||||
m = re.search(rf"(?<!\")(&)?\b(\w+)\b(?!\")", line)
|
||||
if m:
|
||||
for cfuncsig, cppfuncsig in self._transforms.static_funcsigs.items():
|
||||
if m.group(2) == cfuncsig.name:
|
||||
prefix = m.group(1) if m.group(1) is not None else ""
|
||||
line = re.sub(m.re, rf"{prefix}{cppfuncsig.name}", line)
|
||||
debug.line("apply_function_transforms", f"Updating static function {m.group(0)} [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Make sure all container variables have sensible assignments, comparisons etc after any transformations
|
||||
def validate_container_variables(self, line):
|
||||
for k, v in self._transforms.all_args.items():
|
||||
if not v or not arg.is_container(v):
|
||||
continue
|
||||
|
||||
# [1] Assignments
|
||||
m = re.search(rf"\b{v.name}\s*=\s*(\"?\w+\"?).*?;", line)
|
||||
if m:
|
||||
if m.group(1) == "NULL":
|
||||
line = line.replace("NULL", "{}")
|
||||
debug.line("validate_container_variables", f"Updated NULL assigned value [after ]: {line}")
|
||||
elif m.group(1) == "0":
|
||||
# Replace CONTAINER = 0 with CONTAINER.clear()
|
||||
line = re.sub(m.re, f"{v.name}.clear();", line)
|
||||
debug.line("validate_container_variables", f"Changed = 0 assignment to .clear() for container [after ]: {line}")
|
||||
|
||||
# [2] Empty comparisons (== 0)
|
||||
m = re.search(rf"\b{v.name}(\s*==\s*0)\b", line)
|
||||
if m:
|
||||
line = re.sub(m.re, f"{v.name}.empty()", line)
|
||||
debug.line("validate_container_variables", f"Changed == 0 comparison to .empty() for container [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Anything else that's needed
|
||||
def apply_final_checks(self, line):
|
||||
m = re.search(rf"\breturn\s+(\d+)\s*;", line)
|
||||
if m and self._cppfunction.return_type == "GribStatus":
|
||||
line = re.sub(f"{m.group(1)}", f"GribStatus{{{m.group(1)}}}", line)
|
||||
debug.line("apply_final_checks", f"Updated return value to GribStatus [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Override this to provide any function transforms specific to a class
|
||||
def special_function_transforms(self, line):
|
||||
return line
|
||||
|
||||
def update_cpp_line(self, line):
|
||||
|
||||
# Note: These apply in order, be careful if re-arranging!
|
||||
update_functions = [
|
||||
# [1] function updates that expect the original C variable names
|
||||
self.convert_grib_utils,
|
||||
self.apply_function_transforms,
|
||||
self.special_function_transforms,
|
||||
|
||||
# [2] The remaining updates must work with C variables that may have been renamed to C++
|
||||
self.process_variables_initial_pass,
|
||||
self.process_type_declarations,
|
||||
self.process_return_variables,
|
||||
self.process_variable_declarations,
|
||||
self.process_deleted_variables,
|
||||
self.apply_variable_transforms,
|
||||
self.process_function_pointers,
|
||||
self.process_remaining_cargs,
|
||||
self.process_global_cargs,
|
||||
self.convert_grib_values,
|
||||
self.apply_get_set_substitutions,
|
||||
self.validate_container_variables,
|
||||
self.apply_final_checks,
|
||||
]
|
||||
|
||||
debug.line("update_cpp_line", f"--------------------------------------------------------------------------------")
|
||||
debug.line("update_cpp_line", f"PROCESSING: [ {line} ]")
|
||||
|
||||
# We need to run the skip_line() check after each function
|
||||
for update_func in update_functions:
|
||||
if self.skip_line(line):
|
||||
return line
|
||||
|
||||
line = update_func(line)
|
||||
|
||||
return line
|
||||
|
||||
# This is the main entry point for updating the C++ function body...
|
||||
# It can be called recursively to update split lines etc
|
||||
def update_cpp_body(self, lines):
|
||||
new_lines = []
|
||||
|
||||
for line in lines:
|
||||
if not line:
|
||||
# Assume this is a blank line in the input, so retain it for clarity...
|
||||
new_lines.append(line)
|
||||
continue
|
||||
|
||||
# Split comma-separated variable definitions into separate lines
|
||||
# We then call ourself recursively for each split line
|
||||
m = re.match(r"^(\s*\w+\s+)\w+\s*=?\s*(\w+)?,", line)
|
||||
if m:
|
||||
debug.line("update_cpp_body", f"comma-separated vars [before]: {line}")
|
||||
line = line.replace(",", f";\n{m.group(1)}")
|
||||
split_lines = [l for l in line.split("\n")]
|
||||
for line in split_lines:
|
||||
debug.line("update_cpp_body", f"comma-separated vars [after ]: {line}")
|
||||
split_lines = self.update_cpp_body(split_lines)
|
||||
new_lines.extend(split_lines)
|
||||
continue
|
||||
|
||||
line = self.update_cpp_line(line)
|
||||
if line:
|
||||
new_lines.append(line)
|
||||
|
||||
return new_lines
|
||||
|
||||
# Takes the c function, converts the body to C++ code, and returns the lines
|
||||
def create_cpp_body(self):
|
||||
debug.line("create_cpp_body", f"\n============================== {self._cfunction.name} [IN] ==============================\n")
|
||||
cpp_lines = self.update_cpp_body(self._cfunction.code)
|
||||
debug.line("create_cpp_body", f"\n============================== {self._cfunction.name} [OUT] ==============================\n")
|
||||
|
||||
return cpp_lines
|
|
@ -0,0 +1,68 @@
|
|||
import debug
|
||||
import arg
|
||||
import re
|
||||
|
||||
# Represent a function signature
|
||||
class FuncSig:
|
||||
def __init__(self, return_type, name, args, template=None) -> None:
|
||||
self._return_type = return_type
|
||||
self._name = name
|
||||
self._args = args
|
||||
self._template = template
|
||||
self._static = False
|
||||
|
||||
# Try and create a func sig from a string: will return None is unsuccessful...
|
||||
# Note: the sig_string can be multi-line...
|
||||
@classmethod
|
||||
def from_string(cls, sig_string):
|
||||
# Ignore typedefs - we don't want function pointers!
|
||||
if re.match(r"\s*\btypedef", sig_string, re.DOTALL):
|
||||
return None
|
||||
|
||||
sig = None
|
||||
m = re.match(r"(static\s*)?\b([^(]+)\s+(\w+)\s*\(([^(]+)\)", sig_string, re.DOTALL)
|
||||
if m:
|
||||
return_type = m.group(2)
|
||||
name = m.group(3)
|
||||
args = []
|
||||
for entry in [a.strip() for a in m.group(4).split(",")]:
|
||||
if not entry:
|
||||
continue
|
||||
args.append(arg.Arg.from_string(entry))
|
||||
|
||||
sig = cls(return_type, name, args)
|
||||
if sig and m.group(1):
|
||||
sig.static = True
|
||||
|
||||
return sig
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self._args
|
||||
|
||||
@property
|
||||
def return_type(self):
|
||||
return self._return_type
|
||||
|
||||
@property
|
||||
def static(self):
|
||||
return self._static
|
||||
|
||||
@static.setter
|
||||
def static(self, value):
|
||||
self._static = value
|
||||
|
||||
@property
|
||||
def template(self):
|
||||
return self._template
|
||||
|
||||
@template.setter
|
||||
def template(self, value):
|
||||
self._template = value
|
||||
|
||||
def as_string(self):
|
||||
return f"{'static ' if self.static else ''}{self.return_type} {self.name}({', '.join([a.type + ' ' + a.name for a in self.args if a])})"
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
from func import *
|
||||
|
||||
# Specialisation of Function for global functions
|
||||
class GlobalFunction(Function):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
||||
# Overridden as there's no opening and closing { } so don't want to assert!
|
||||
@property
|
||||
def code(self):
|
||||
return self._lines
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
from func_conv import *
|
||||
import global_func
|
||||
|
||||
# Specialisation of FunctionConverter for global functions
|
||||
class GlobalFunctionConverter(FunctionConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return global_func.GlobalFunction(cppfuncsig)
|
||||
|
||||
# If the line starts [FORWARD_DECLARATION] then it is a placeholder from the file parser
|
||||
# We need to change it to a forward declaration of the transformed function
|
||||
def special_function_transforms(self, line):
|
||||
m = re.match(rf"^\[FORWARD_DECLARATION\](\w+)", line)
|
||||
if m:
|
||||
debug.line("special_function_transforms",f"[FORWARD_DECLARATION] name={m.group(1)}")
|
||||
for cfuncsig, cppfuncsig in self._transforms.static_funcsigs.items():
|
||||
if cfuncsig.name == m.group(1):
|
||||
line = f"{cppfuncsig.as_string()};"
|
||||
debug.line("special_function_transforms",f"[FORWARD_DECLARATION] {line}")
|
||||
return line
|
||||
|
||||
line = ""
|
||||
debug.line("special_function_transforms",f"Not a static function forward declaration - removing line: {m.group(0)}")
|
||||
|
||||
return super().special_function_transforms(line)
|
|
@ -0,0 +1,137 @@
|
|||
import debug
|
||||
import member
|
||||
import func
|
||||
import global_func
|
||||
import static_func
|
||||
import constructor_method
|
||||
import destructor_method
|
||||
import inherited_method
|
||||
import private_method
|
||||
import os
|
||||
import re
|
||||
|
||||
# Represents the C code parsed from a grib_accessor_class*cc file
|
||||
class GribAccessor:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
path,
|
||||
functions,
|
||||
includes,
|
||||
definitions
|
||||
):
|
||||
assert "CLASS" in definitions, "No CLASS defined"
|
||||
assert "SUPER" in definitions, "No SUPER defined"
|
||||
assert "FACTORY" in definitions, "No FACTORY defined"
|
||||
|
||||
self._name = self.extract_class_name(path)
|
||||
self._class = definitions["CLASS"][0]
|
||||
self._super = definitions["SUPER"][0]
|
||||
self._factory_name = definitions["FACTORY"][0]
|
||||
self._members = [member.Member.from_string(m) for m in definitions.get("MEMBERS", []) if m != ""]
|
||||
self._implements = definitions.get("IMPLEMENTS", [])
|
||||
self._body_includes = includes
|
||||
self.classify_functions(functions)
|
||||
|
||||
class_name = "Accessor"
|
||||
type_name = "grib_accessor"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def class_name(self):
|
||||
return self._class
|
||||
|
||||
@property
|
||||
def super(self):
|
||||
return self._super
|
||||
|
||||
@property
|
||||
def factory_name(self):
|
||||
return self._factory_name
|
||||
|
||||
@property
|
||||
def members(self):
|
||||
return self._members
|
||||
|
||||
@property
|
||||
def global_function(self):
|
||||
return self._global_function
|
||||
|
||||
@property
|
||||
def constructor(self):
|
||||
return self._constructor
|
||||
|
||||
@property
|
||||
def destructor(self):
|
||||
return self._destructor if self._destructor else ""
|
||||
|
||||
@property
|
||||
def inherited_methods(self):
|
||||
return self._inherited_methods
|
||||
|
||||
@property
|
||||
def private_methods(self):
|
||||
return self._private_methods
|
||||
|
||||
@property
|
||||
def static_functions(self):
|
||||
return self._static_functions
|
||||
|
||||
def extract_class_name(self, path):
|
||||
path, _ = os.path.splitext(path)
|
||||
name = os.path.basename(path)
|
||||
return name
|
||||
|
||||
# Sort all the passed in functions into the correct "bins"
|
||||
def classify_functions(self, functions):
|
||||
self._inherited_methods = []
|
||||
self._private_methods = []
|
||||
self._static_functions = []
|
||||
self._constructor = None
|
||||
self._destructor = None
|
||||
|
||||
# Note: Using list(...) creates a static list of (key, value) pairs from the self._functions dict to iterate over
|
||||
# This allows for safe modifications (deletions in this case) to the dictionary within the loop.
|
||||
for name, f in list(functions.items()):
|
||||
if type(f) is global_func.GlobalFunction:
|
||||
self._global_function = functions.pop(name)
|
||||
elif type(f) is constructor_method.ConstructorMethod:
|
||||
self._constructor = functions.pop(name)
|
||||
elif type(f) is destructor_method.DestructorMethod:
|
||||
self._destructor = functions.pop(name)
|
||||
elif type(f) is inherited_method.InheritedMethod:
|
||||
self._inherited_methods.append(functions.pop(name))
|
||||
elif type(f) is private_method.PrivateMethod:
|
||||
self._private_methods.append(functions.pop(name))
|
||||
elif type(f) is static_func.StaticFunction:
|
||||
self._static_functions.append(functions.pop(name))
|
||||
|
||||
# Check we processed all functions!
|
||||
assert len(functions) == 0, sorted(functions.keys())
|
||||
|
||||
# Factory to create the correct Function based on the supplied definitions
|
||||
def create_cfunction(func_sig, definitions):
|
||||
cfunction = None
|
||||
|
||||
if func_sig.name == func.global_function_name:
|
||||
cfunction = global_func.GlobalFunction(func_sig)
|
||||
elif func_sig.name == "init":
|
||||
cfunction = constructor_method.ConstructorMethod(func_sig)
|
||||
elif func_sig.name == "destroy":
|
||||
cfunction = destructor_method.DestructorMethod(func_sig)
|
||||
elif func_sig.name in definitions.get("IMPLEMENTS", []):
|
||||
cfunction = inherited_method.InheritedMethod(func_sig)
|
||||
|
||||
if not cfunction:
|
||||
# If any arg starts with a "ptr type name", then it's a private method (as we've already extracted inherited functions)
|
||||
for arg in func_sig.args:
|
||||
if re.search(r"grib_accessor(\w*)?\*", arg.type):
|
||||
cfunction = private_method.PrivateMethod(func_sig)
|
||||
|
||||
if not cfunction:
|
||||
cfunction = static_func.StaticFunction(func_sig)
|
||||
|
||||
return cfunction
|
|
@ -0,0 +1,205 @@
|
|||
import debug
|
||||
from grib_accessor import GribAccessor
|
||||
from accessor_data import AccessorData
|
||||
import arg
|
||||
import arg_conv
|
||||
import member_conv
|
||||
import global_func_conv
|
||||
import constructor_method
|
||||
import constructor_method_conv
|
||||
import funcsig
|
||||
import destructor_method
|
||||
import destructor_method_conv
|
||||
import inherited_method_conv
|
||||
import private_method_conv
|
||||
import static_func_conv
|
||||
import transforms
|
||||
import re
|
||||
|
||||
prefix = "grib_accessor_class_"
|
||||
rename = {
|
||||
"Gen": "Accessor", # "Generic",
|
||||
"Md5": "Md5Sum", # We rename because of macos case insensitive file system
|
||||
"Assert": "Assertion", # Name clash with assert.h
|
||||
}
|
||||
|
||||
common_includes = [
|
||||
"AccessorFactory.h",
|
||||
"AccessorUtils/ConversionHelper.h",
|
||||
"AccessorUtils/GribUtils.h",
|
||||
"AccessorUtils/GribAccessorFlag.h",
|
||||
"AccessorUtils/AccessorException.h"
|
||||
]
|
||||
|
||||
non_const_cmethods = [
|
||||
"pack_long",
|
||||
"pack_missing",
|
||||
"pack_string",
|
||||
"pack_double",
|
||||
"pack_bytes",
|
||||
"pack_expression",
|
||||
"pack_string_array",
|
||||
"update_size",
|
||||
"notify_change",
|
||||
]
|
||||
|
||||
# These inherited methods will not be converted
|
||||
excluded_inherited_methods = [
|
||||
"next_offset"
|
||||
]
|
||||
|
||||
# Convert GribAccessor to AccessorData
|
||||
class GribAccessorConverter:
|
||||
def __init__(self, grib_accessor_instance):
|
||||
self._grib_accessor = grib_accessor_instance
|
||||
self._accessor_data = None
|
||||
self._transforms = None
|
||||
|
||||
# Convert GribAccessor to AccessorData
|
||||
def to_accessor_data(self, other_grib_accessors):
|
||||
debug.line("to_accessor_data", f"\n===== [CONVERTING:BEGIN] {self._grib_accessor.name} ====================\n")
|
||||
|
||||
self.other_grib_accessors = other_grib_accessors
|
||||
self.create_accessor_data()
|
||||
self.create_transforms()
|
||||
self.add_global_function()
|
||||
self.add_includes()
|
||||
self.add_members()
|
||||
self.add_constructor_method()
|
||||
self.add_destructor_method()
|
||||
self.add_inherited_methods()
|
||||
self.add_private_methods()
|
||||
self.add_static_functions()
|
||||
self.add_forward_declarations()
|
||||
|
||||
debug.line("to_accessor_data", f"\n===== [CONVERTING:END] {self._grib_accessor.name} ====================\n")
|
||||
|
||||
return self._accessor_data
|
||||
|
||||
def create_accessor_data(self):
|
||||
cpp_name = self.transform_class_name(self._grib_accessor.name)
|
||||
cpp_super_name = self.transform_class_name(self._grib_accessor.super)
|
||||
# Need to keep the C name for the factory as it is called from C code...
|
||||
cpp_factory_name = self._grib_accessor.factory_name
|
||||
|
||||
self._accessor_data = AccessorData(cpp_name, cpp_super_name, cpp_factory_name)
|
||||
|
||||
# Get members all the way up the hierarchy
|
||||
def members_in_hierarchy(self, grib_accessor_inst):
|
||||
result = set()
|
||||
for m in grib_accessor_inst.members:
|
||||
result.add(m)
|
||||
if grib_accessor_inst.super in self.other_grib_accessors:
|
||||
result.update(self.members_in_hierarchy(self.other_grib_accessors[grib_accessor_inst.super]))
|
||||
return result
|
||||
|
||||
def create_transforms(self):
|
||||
self._transforms = transforms.Transforms(types=arg_conv.common_type_transforms)
|
||||
|
||||
self._transforms.add_to_class_types("self", self._grib_accessor.name, self._accessor_data.name)
|
||||
self._transforms.add_to_class_types("super", self._grib_accessor.super, self._accessor_data.super)
|
||||
|
||||
# Create funcsig transforms for all C funcs
|
||||
for func in self._grib_accessor.inherited_methods:
|
||||
if func.name not in excluded_inherited_methods:
|
||||
converter = inherited_method_conv.InheritedMethodConverter()
|
||||
cppfuncsig = converter.to_cpp_func_sig(func.func_sig)
|
||||
self._transforms.add_to_inherited_funcsigs(func.func_sig, cppfuncsig)
|
||||
|
||||
for func in self._grib_accessor.private_methods:
|
||||
converter = private_method_conv.PrivateMethodConverter()
|
||||
cppfuncsig = converter.to_cpp_func_sig(func.func_sig)
|
||||
self._transforms.add_to_private_funcsigs(func.func_sig, cppfuncsig)
|
||||
|
||||
for func in self._grib_accessor.static_functions:
|
||||
converter = static_func_conv.StaticFunctionConverter()
|
||||
cppfuncsig = converter.to_cpp_func_sig(func.func_sig)
|
||||
self._transforms.add_to_static_funcsigs(func.func_sig, cppfuncsig)
|
||||
|
||||
def add_global_function(self):
|
||||
global_func_converter = global_func_conv.GlobalFunctionConverter()
|
||||
self._accessor_data.global_function = global_func_converter.to_cpp_function(self._grib_accessor.global_function, self._transforms)
|
||||
self._transforms.make_global()
|
||||
|
||||
def add_includes(self):
|
||||
# Header includes
|
||||
if self._accessor_data.super == "AccessorData":
|
||||
self._accessor_data.add_header_include("/".join(["AccessorData", self._accessor_data.super + ".h"]))
|
||||
else:
|
||||
self._accessor_data.add_header_include(f"{self._accessor_data.super}.h")
|
||||
|
||||
# Source includes
|
||||
self._accessor_data.add_source_include(f"{self._accessor_data.name}.h")
|
||||
|
||||
for inc in common_includes:
|
||||
self._accessor_data.add_source_include(inc)
|
||||
|
||||
# Adds all members for this accessor to the accessor_data object,
|
||||
# And also stores ALL members in the hierarchy in the transform
|
||||
def add_members(self):
|
||||
for cmember in self.members_in_hierarchy(self._grib_accessor):
|
||||
member_converter = member_conv.MemberConverter(cmember)
|
||||
cppmember = member_converter.to_cpp_arg()
|
||||
if cmember in self._grib_accessor.members:
|
||||
self._accessor_data.add_member(cppmember)
|
||||
self._transforms.add_to_members(cmember, cppmember)
|
||||
|
||||
def add_constructor_method(self):
|
||||
# Create a default constructor if none exists
|
||||
if self._grib_accessor.constructor:
|
||||
constructor_method_converter = constructor_method_conv.ConstructorMethodConverter()
|
||||
self._accessor_data._constructor = constructor_method_converter.to_cpp_function(self._grib_accessor.constructor, self._transforms)
|
||||
else:
|
||||
constructor_sig = funcsig.FuncSig(None, "init", [])
|
||||
self._accessor_data._constructor = constructor_method.ConstructorMethod(constructor_sig)
|
||||
|
||||
def add_destructor_method(self):
|
||||
# Create a default destructor if none exists
|
||||
if self._grib_accessor.destructor:
|
||||
destructor_method_converter = destructor_method_conv.DestructorMethodConverter()
|
||||
self._accessor_data._destructor = destructor_method_converter.to_cpp_function(self._grib_accessor.destructor, self._transforms)
|
||||
else:
|
||||
destructor_sig = funcsig.FuncSig(None, "destroy", [])
|
||||
self._accessor_data._destructor = destructor_method.DestructorMethod(destructor_sig)
|
||||
|
||||
def add_inherited_methods(self):
|
||||
for cfunc in self._grib_accessor.inherited_methods:
|
||||
if cfunc.name not in excluded_inherited_methods:
|
||||
inherited_method_converter = inherited_method_conv.InheritedMethodConverter()
|
||||
cppfunc = inherited_method_converter.to_cpp_function(cfunc, self._transforms)
|
||||
cppfunc.const = cfunc.name not in non_const_cmethods
|
||||
self._accessor_data.add_inherited_method(cppfunc)
|
||||
|
||||
def add_private_methods(self):
|
||||
for cfunc in self._grib_accessor.private_methods:
|
||||
private_method_converter = private_method_conv.PrivateMethodConverter()
|
||||
cppfunc = private_method_converter.to_cpp_function(cfunc, self._transforms)
|
||||
cppfunc.const = cfunc.name not in non_const_cmethods
|
||||
self._accessor_data.add_private_method(cppfunc)
|
||||
|
||||
def add_static_functions(self):
|
||||
for cfunc in self._grib_accessor.static_functions:
|
||||
static_function_converter = static_func_conv.StaticFunctionConverter()
|
||||
cppfunc = static_function_converter.to_cpp_function(cfunc, self._transforms)
|
||||
self._accessor_data.add_static_function(cppfunc)
|
||||
|
||||
# Search for argument types used in private methods where the type is defined in the .cc
|
||||
# file and so needs a forward declaration in the header
|
||||
def add_forward_declarations(self):
|
||||
for global_arg in self._transforms.global_args.values():
|
||||
|
||||
if not global_arg:
|
||||
continue
|
||||
|
||||
for cppfunc in self._accessor_data.private_methods:
|
||||
for cpp_arg in cppfunc.args:
|
||||
if cpp_arg and cpp_arg.underlying_type == global_arg.name:
|
||||
debug.line("add_forward_declarations", f"Found a forward declaration: Method: {cppfunc.name} arg: {arg.arg_string(cpp_arg)} declaration: {arg.arg_string(global_arg)}")
|
||||
self._accessor_data.add_forward_declaration(arg.arg_string(global_arg)+";")
|
||||
|
||||
|
||||
# Convert e.g. grib_accessor_class_proj_string to ProjString
|
||||
def transform_class_name(self, name):
|
||||
name = name.replace(prefix, "")
|
||||
name = arg_conv.transform_type_name(name)
|
||||
return rename.get(name, name) + "Data"
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
from method import *
|
||||
import member_function_transforms
|
||||
|
||||
# Specialisation of AccessorData method
|
||||
class InheritedMethod(Method):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
from method_conv import *
|
||||
import inherited_method
|
||||
|
||||
# Specialisation of MethodConverter for AccessorData Methods
|
||||
class InheritedMethodConverter(MethodConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return inherited_method.InheritedMethod(cppfuncsig)
|
||||
|
||||
def to_cpp_name(self, cfuncsig):
|
||||
name = member_function_transforms.transformed_name(cfuncsig.name)
|
||||
if name:
|
||||
return name
|
||||
else:
|
||||
debug.line("to_cpp_name", f"Calling super...")
|
||||
return super().to_cpp_name(cfuncsig)
|
||||
|
||||
def to_cpp_return_type(self, cfuncsig):
|
||||
ret = member_function_transforms.return_type(cfuncsig.name)
|
||||
if ret:
|
||||
return ret
|
||||
else:
|
||||
return super().to_cpp_return_type(cfuncsig)
|
||||
|
||||
def to_cpp_args(self, cfuncsig):
|
||||
cppargs = member_function_transforms.transformed_args(cfuncsig.name)
|
||||
|
||||
if cppargs:
|
||||
return cppargs
|
||||
else:
|
||||
return super().to_cpp_args(cfuncsig)
|
||||
|
||||
# Special-handling for lengths. The len C param is removed because we use containers, however we need to
|
||||
# deal with size-related code
|
||||
def process_len_arg(self, line):
|
||||
if len(self._cfunction.args) < 3:
|
||||
return line
|
||||
|
||||
len_carg = self._cfunction.args[2]
|
||||
if len_carg.name != "len":
|
||||
return line
|
||||
|
||||
cppargs = member_function_transforms.transformed_args(self._cfunction.name)
|
||||
if not cppargs:
|
||||
return license
|
||||
|
||||
len_cpparg = cppargs[1]
|
||||
|
||||
# Note: Some code uses len[0] instead of *len, so we check for both...
|
||||
|
||||
# Replace *len = N with CONTAINER.clear() if N=0, or CONTAINER.resize() the line if N is any other value
|
||||
m = re.search(rf"\*?\b{len_carg.name}\b(\[0\])?\s*=\s*(\w+).*?;", line)
|
||||
if m:
|
||||
if len_cpparg.is_const():
|
||||
line = re.sub(rf"^(\s*)", rf"// [length assignment removed - var is const] \1", line)
|
||||
debug.line("process_len_arg", f"Removed len assignment for const variable [after]: {line}")
|
||||
elif m.group(2) == "0":
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{len_cpparg.name}.clear();", line)
|
||||
debug.line("process_len_arg", f"Replaced *len = 0 with .clear() [after]: {line}")
|
||||
else:
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{len_cpparg.name}.resize({m.group(2)});", line)
|
||||
debug.line("process_len_arg", f"Replaced *len = N with .resize(N) [after]: {line}")
|
||||
|
||||
# Replace *len <=> N with CONTAINER.size() <=> N
|
||||
m = re.search(rf"\*?\b{len_carg.name}\b(\[0\])?\s*([<>!=]=?)\s*(\w+)", line)
|
||||
if m:
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{len_cpparg.name}.size() {m.group(2)} {m.group(3)}", line)
|
||||
debug.line("process_len_arg", f"Replaced *len <=> N with .size() <=> N [after]: {line}")
|
||||
|
||||
# Replace any other *len with CONTAINER.size() <=> N
|
||||
m = re.search(rf"\*?\b{len_carg.name}\b(\[0\])?", line)
|
||||
if m:
|
||||
line = re.sub(rf"{re.escape(m.group(0))}", rf"{len_cpparg.name}.size()", line)
|
||||
debug.line("process_len_arg", f"Replaced *len <=> N with .size() <=> N [after]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Override this to provide any initial conversions before the main update_line runs
|
||||
def process_variables_initial_pass(self, line):
|
||||
|
||||
line = self.process_len_arg(line)
|
||||
|
||||
return super().process_variables_initial_pass(line)
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* (C) Copyright 2005- ECMWF.
|
||||
*
|
||||
* This software is licensed under the terms of the Apache Licence Version 2.0
|
||||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
||||
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
||||
*/
|
||||
|
||||
#include "{{ c.include_header }}"
|
||||
{%- for i in c.common_includes %}
|
||||
#include "{{ i }}"
|
||||
{%- endfor %}
|
||||
|
||||
// converted from {{ c.cname }}.cc
|
||||
|
||||
{%- for i in c.body_includes %}
|
||||
#include {{ i }}
|
||||
{%- endfor %}
|
||||
|
||||
namespace {{ c.nested_namespaces }} {
|
||||
|
||||
// Global section - START
|
||||
{{ c.global_function.body }}
|
||||
// Global section - END
|
||||
|
||||
{% for m in c.static_functions %}
|
||||
static {{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{{ c.name }}::{{ c.name }}({{c.constructor.cpp_args}}) : {{ c.super }}({{ c.constructor.call_args }}){
|
||||
{{ c.constructor.body }}
|
||||
}
|
||||
|
||||
// {{ c.name }}::~{{ c.name }}() { {{ c.destructor.body }} }
|
||||
|
||||
{% for m in c.inherited_methods %}
|
||||
{{ m.template }}
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for m in c.private_methods %}
|
||||
{{ m.template }}
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
namespace {
|
||||
AccessorBuilder<{{ c.name }}> {{ c.name_camel_case }}Builder(AccessorType("{{ c.factory_name }}"));
|
||||
}
|
||||
|
||||
{% for n in c.namespaces_reversed %}} // namespace {{ n }}
|
||||
{%- endfor %}
|
|
@ -8,17 +8,10 @@
|
|||
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
||||
*/
|
||||
|
||||
#include "{{ c.include_header }}"
|
||||
{%- for i in c.common_includes %}
|
||||
{%- for i in c.source_includes %}
|
||||
#include "{{ i }}"
|
||||
{%- endfor %}
|
||||
|
||||
// converted from {{ c.cname }}.cc
|
||||
|
||||
{%- for i in c.body_includes %}
|
||||
#include {{ i }}
|
||||
{%- endfor %}
|
||||
|
||||
namespace {{ c.nested_namespaces }} {
|
||||
|
||||
// Global section - START
|
||||
|
@ -26,12 +19,12 @@ namespace {{ c.nested_namespaces }} {
|
|||
// Global section - END
|
||||
|
||||
{% for m in c.static_functions %}
|
||||
static {{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) {
|
||||
static {{ m.return_type }} {{ m.name }}({{ m.arg_string }}) {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{{ c.name }}::{{ c.name }}({{c.constructor.cpp_args}}) : {{ c.super }}({{ c.constructor.call_args }}){
|
||||
{{ c.name }}::{{ c.name }}({{c.constructor.arg_string}}) : {{ c.super }}({{ c.constructor.arg_name_string }}){
|
||||
{{ c.constructor.body }}
|
||||
}
|
||||
|
||||
|
@ -39,21 +32,20 @@ static {{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) {
|
|||
|
||||
{% for m in c.inherited_methods %}
|
||||
{{ m.template }}
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} {
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.name }}({{ m.arg_string }}) {{ m.const }} {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for m in c.private_methods %}
|
||||
{{ m.template }}
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} {
|
||||
{{ m.return_type }} {{ c.name }}::{{ m.name }}({{ m.arg_string }}) {{ m.const }} {
|
||||
{{ m.body }}
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
namespace {
|
||||
AccessorBuilder<{{ c.name }}> {{ c.name_camel_case }}Builder(AccessorType("{{ c.factory_name }}"));
|
||||
AccessorBuilder<{{ c.name }}> {{ c.camel_case_name }}Builder(AccessorType("{{ c.factory_name }}"));
|
||||
}
|
||||
|
||||
{% for n in c.namespaces_reversed %}} // namespace {{ n }}
|
||||
{%- endfor %}
|
||||
} // namespace {{ c.nested_namespaces }}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* (C) Copyright 2005- ECMWF.
|
||||
*
|
||||
* This software is licensed under the terms of the Apache Licence Version 2.0
|
||||
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
||||
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "{{ c.include_super }}"
|
||||
|
||||
{% for i in c.header_includes %}
|
||||
#include {{ i }}
|
||||
{%- endfor %}
|
||||
|
||||
namespace {{ c.nested_namespaces }} {
|
||||
|
||||
{%- for f in c.forward_declarations %}
|
||||
{{ f }};
|
||||
{%- endfor %}
|
||||
|
||||
class {{ c.name }} : public {{ c.super }} {
|
||||
public:
|
||||
{{ c.name }}({{c.constructor.cpp_args}});
|
||||
~{{ c.name }}() = default;
|
||||
|
||||
private:
|
||||
// Inherited functions
|
||||
{%- for m in c.inherited_methods %}
|
||||
{{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} override;{% endfor %}
|
||||
|
||||
// Members
|
||||
{% for m in c.members %} {{ m.as_declaration() }}{{ m.default_value }};
|
||||
{%- endfor %}
|
||||
|
||||
// Private methods
|
||||
{%- for m in c.private_methods %}
|
||||
{{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) const;{% endfor %}
|
||||
};
|
||||
|
||||
|
||||
|
||||
{% for n in c.namespaces_reversed %}} // namespace {{ n }}
|
||||
{%- endfor %}
|
|
@ -10,38 +10,34 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "{{ c.include_super }}"
|
||||
|
||||
{% for i in c.header_includes %}
|
||||
#include {{ i }}
|
||||
{% for i in c.header_includes -%}
|
||||
#include "{{ i }}"
|
||||
{%- endfor %}
|
||||
|
||||
namespace {{ c.nested_namespaces }} {
|
||||
|
||||
// Forward declarations
|
||||
{%- for f in c.forward_declarations %}
|
||||
{{ f }};
|
||||
{{ f }}
|
||||
{%- endfor %}
|
||||
|
||||
class {{ c.name }} : public {{ c.super }} {
|
||||
public:
|
||||
{{ c.name }}({{c.constructor.cpp_args}});
|
||||
{{ c.name }}({{c.constructor.arg_string}});
|
||||
~{{ c.name }}() = default;
|
||||
|
||||
private:
|
||||
// Inherited functions
|
||||
{%- for m in c.inherited_methods %}
|
||||
{{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) {{ m.const }} override;{% endfor %}
|
||||
{{ m.return_type }} {{ m.name }}({{ m.arg_string }}) {{ m.const }} override;{% endfor %}
|
||||
|
||||
// Members
|
||||
{% for m in c.members %}{{ m.mutable }} {{ m.type }} {{ m.name }}{{ m.array }}{{ m.default_value }};
|
||||
{% for m in c.members %} {{ m.as_declaration() }}{{ m.default_value }};
|
||||
{%- endfor %}
|
||||
|
||||
// Private methods
|
||||
{%- for m in c.private_methods %}
|
||||
{{ m.return_type }} {{ m.transformed_name }}({{ m.cpp_args }}) const;{% endfor %}
|
||||
{{ m.return_type }} {{ m.name }}({{ m.arg_string }}) const;{% endfor %}
|
||||
};
|
||||
|
||||
|
||||
|
||||
{% for n in c.namespaces_reversed %}} // namespace {{ n }}
|
||||
{%- endfor %}
|
||||
} // namespace {{ c.nested_namespaces }}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import arg
|
||||
|
||||
class Member(arg.Arg):
|
||||
def __init__(self, type, name=""):
|
||||
super().__init__(type, name)
|
||||
self.default_value = ""
|
||||
self._mutable = False
|
|
@ -0,0 +1,39 @@
|
|||
# Convert Member from C to C++
|
||||
|
||||
import member
|
||||
import arg_conv
|
||||
import debug
|
||||
import re
|
||||
|
||||
class MemberConverter(arg_conv.ArgConverter):
|
||||
def __init__(self, carg):
|
||||
super().__init__(carg)
|
||||
|
||||
# Override for extra changes required for a member, such as ending with "_"
|
||||
# Need to ensure we return a member, not an arg!
|
||||
def to_cpp_arg(self, type_transforms=arg_conv.common_type_transforms):
|
||||
cppmember = None
|
||||
|
||||
# If then type is grib_accessor_ pointer, then we'll convert to an AccessorName
|
||||
if re.search(r"grib_accessor\w*\*", self._carg.type):
|
||||
cppname = arg_conv.transform_variable_name(self._carg.name) + "_"
|
||||
cppmember = member.Member("AccessorName", cppname)
|
||||
cppmember.default_value = "{\"\"}"
|
||||
else:
|
||||
cpp_arg = super().to_cpp_arg(type_transforms)
|
||||
if cpp_arg:
|
||||
cppmember = member.Member(cpp_arg.type, cpp_arg.name + "_")
|
||||
|
||||
# We'll assume "const char*" means this variable refers to another accessor...
|
||||
if self._carg.type == "const char*":
|
||||
cppmember.type = "AccessorName"
|
||||
cppmember.default_value = "{\"\"}"
|
||||
else:
|
||||
cppmember.default_value = ""
|
||||
|
||||
cppmember._mutable = False
|
||||
|
||||
return cppmember
|
||||
|
||||
def to_cpp_func_sig_arg(self, type_transforms = arg_conv.common_type_transforms):
|
||||
assert False, "to_cpp_func_sig_arg not supported for members"
|
|
@ -1,15 +1,9 @@
|
|||
# Transforms that apply to AccessorData member functions
|
||||
|
||||
import re
|
||||
from convert_debug import debug_line
|
||||
from arg_transforms import Arg
|
||||
|
||||
# Represent a function signature
|
||||
class FuncSig:
|
||||
def __init__(self, ret, name, args) -> None:
|
||||
self.ret = ret
|
||||
self.name = name
|
||||
self.args = args
|
||||
import debug
|
||||
from arg import Arg
|
||||
from funcsig import FuncSig
|
||||
|
||||
# The following static dictionary defines well-known conversions from C to the equivalent
|
||||
# AccessorData virtual functions
|
||||
|
@ -70,11 +64,21 @@ accessor_member_func_conversions = {
|
|||
"byte_count" : FuncSig("long", "byteCount", [None]),
|
||||
# static long byte_offset(grib_accessor*);
|
||||
"byte_offset" : FuncSig("long", "byteOffset", [None]),
|
||||
|
||||
# Other functions
|
||||
# static void dump(grib_accessor*, grib_dumper*);
|
||||
"dump" : FuncSig("void", "dump", [None, None]),
|
||||
# static int compare(grib_accessor*, grib_accessor*);
|
||||
"compare" : FuncSig("bool", "compare", [None, Arg("AccessorData const&", "rhs")]),
|
||||
# static grib_accessor* make_clone(grib_accessor*, grib_section*, int*);
|
||||
"make_clone" : FuncSig("AccessorDataPtr", "clone", [None, None, None]),
|
||||
# static int is_missing(grib_accessor* a)
|
||||
"is_missing" : FuncSig("bool", "isMissing", [None]),
|
||||
}
|
||||
|
||||
def return_type(func_name):
|
||||
if func_name in accessor_member_func_conversions:
|
||||
return accessor_member_func_conversions[func_name].ret
|
||||
return accessor_member_func_conversions[func_name].return_type
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
from func import *
|
||||
|
||||
# Specialisation of Function for AccessorData methods
|
||||
class Method(Function):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
||||
self._const = False
|
||||
#self._owner_arg_type = self._owner_class.name
|
||||
#self._owner_arg_name = arg.transform_variable_name(self._owner_arg_type)
|
||||
|
||||
@property
|
||||
def const(self):
|
||||
return "const" if self._const else ""
|
||||
|
||||
@const.setter
|
||||
def const(self, value):
|
||||
self._const = value
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
from func_conv import *
|
||||
import method
|
||||
import member_function_transforms
|
||||
|
||||
# Define base class member mapping
|
||||
base_members_map = {
|
||||
arg.Arg("long","length") : arg.Arg("long","length_"),
|
||||
arg.Arg("long","offset") : arg.Arg("long","offset_"),
|
||||
arg.Arg("unsigned long","flags") : arg.Arg("unsigned long","flags_"),
|
||||
arg.Arg("int","dirty") : arg.Arg("int","dirty_"),
|
||||
arg.Arg("grib_virtual_value*","vvalue") : arg.Arg("std::unique_ptr<grib_virtual_value>","vvalue_"),
|
||||
arg.Arg("const char*","set") : arg.Arg("std::string","set_")
|
||||
}
|
||||
|
||||
# Specialisation of FunctionConverter for AccessorData Methods
|
||||
class MethodConverter(FunctionConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return method.Method(cppfuncsig)
|
||||
|
||||
# Extra processing required for grib_handle members that are referenced
|
||||
def update_grib_handle_members(self, line):
|
||||
|
||||
for k in self._transforms.all_args.keys():
|
||||
if k.type == "grib_handle*":
|
||||
m = re.search(rf"\b{k.name}->buffer", line)
|
||||
if m:
|
||||
line = re.sub(rf"{m.group(0)}->data", "buffer_.data()", line)
|
||||
debug.line("update_grib_handle_members", f"Updated buffer ref [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
def update_class_members(self, line):
|
||||
line,count = re.subn(r"\bsuper->\b", f"{self._transforms.class_types['super'].cppvalue}::", line)
|
||||
if count:
|
||||
debug.line("update_class_members", f"begin [after ]: {line}")
|
||||
|
||||
accessor_variable_name = arg_conv.transform_variable_name(self._transforms.class_types["self"].cppvalue)
|
||||
|
||||
# Replace name member with a string literal (it's only used in logging)
|
||||
line,count = re.subn(r"\ba->name", f"\"{self._transforms.class_types['super'].cppvalue}\"", line)
|
||||
if count:
|
||||
debug.line("update_class_members", f"Changed a->name to string literal [after ]: {line}")
|
||||
|
||||
for n in [r"\bself\b", r"\ba\b"]:
|
||||
line,count = re.subn(n, f"{accessor_variable_name}", line)
|
||||
if count:
|
||||
debug.line("update_class_members", f"this [after ]: {line}")
|
||||
|
||||
if re.match(rf".*\b{accessor_variable_name}\s+=", line):
|
||||
debug.line("update_class_members", f"deleting: {line}")
|
||||
line = ""
|
||||
|
||||
if re.match(rf".*\bsuper\s+=\s+\*\({accessor_variable_name}->cclass->super\)", line):
|
||||
debug.line("update_class_members", f"deleting: {line}")
|
||||
line = ""
|
||||
|
||||
for k,v in base_members_map.items():
|
||||
line,count = re.subn(rf"\b{accessor_variable_name}->{k.name}\b", rf"{v.name}", line)
|
||||
if(count):
|
||||
debug.line("update_class_members", f"base_members_map [after ]: {line}")
|
||||
|
||||
# Process all members in the hierarchy
|
||||
m = re.search(rf"\b{accessor_variable_name}->(\w+)\b", line)
|
||||
if m:
|
||||
for k,v in self._transforms.members.items():
|
||||
assert v is not None, f"v is None for k={arg.arg_string(k)}"
|
||||
if m.group(1) == k.name:
|
||||
line = re.sub(m.re, rf"{v.name}", line)
|
||||
debug.line("update_class_members", f"members_in_hierarchy [after ]: {line}")
|
||||
|
||||
m = re.search(rf"\b(\w+)\s*\(\s*([^,]+),", line)
|
||||
if m:
|
||||
for f in self._transforms.private_funcsigs.keys():
|
||||
if m.group(1) == f.name:
|
||||
line = re.sub(m.re, f"{accessor_variable_name}.{f.name}(", line)
|
||||
debug.line("update_class_members", f"private_methods [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
|
||||
# Override this to provide any initial conversions before the main update_line runs
|
||||
def process_variables_initial_pass(self, line):
|
||||
line = self.update_class_members(line)
|
||||
line = self.update_grib_handle_members(line)
|
||||
return super().process_variables_initial_pass(line)
|
||||
|
||||
# Convert grib_(un)pack_TYPE calls to the equivalent member function
|
||||
# Note: We don't convert the vars here, but we do remove those not needed in the C++ function
|
||||
# We also add a call to explicitly set the size var as it may be used after this call...
|
||||
def convert_grib_un_pack_functions(self, line):
|
||||
m = re.search(rf"\bgrib_((?:un)?pack_\w+)\((.*)\)", line)
|
||||
if m:
|
||||
buffer_index, size_index = member_function_transforms.c_buffer_and_size_index(m.group(1))
|
||||
|
||||
if buffer_index and size_index:
|
||||
func_name = member_function_transforms.transformed_name(m.group(1))
|
||||
vars = m.group(2).split(",")
|
||||
line = re.sub(m.re, rf"{func_name}({vars[buffer_index]}); {vars[size_index]} = {vars[buffer_index]}.size()", line)
|
||||
debug.line("convert_grib_un_pack_functions", f"Converted grib_{m.group(1)} function: [after ]: {line}")
|
||||
|
||||
return line
|
||||
|
||||
# Overridden to apply member function substitutions
|
||||
def apply_function_transforms(self, line):
|
||||
|
||||
line = self.convert_grib_un_pack_functions(line)
|
||||
|
||||
m = re.search(rf"(?<!\")(&)?\b(\w+)\b(?!\")", line)
|
||||
if m:
|
||||
for cfuncsig, cppfuncsig in self._transforms.inherited_funcsigs.items():
|
||||
if m.group(2) == cfuncsig.name:
|
||||
prefix = m.group(1) if m.group(1) is not None else ""
|
||||
line = re.sub(m.re, rf"{prefix}{cppfuncsig.name}", line)
|
||||
debug.line("apply_function_transforms", f"Updating inherited method {m.group(0)} [after ]: {line}")
|
||||
|
||||
for cfuncsig, cppfuncsig in self._transforms.private_funcsigs.items():
|
||||
if m.group(2) == cfuncsig.name:
|
||||
prefix = m.group(1) if m.group(1) is not None else ""
|
||||
line = re.sub(m.re, rf"{prefix}{cppfuncsig.name}", line)
|
||||
debug.line("apply_function_transforms", f"Updating private method {m.group(0)} [after ]: {line}")
|
||||
|
||||
return super().apply_function_transforms(line)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
from method import *
|
||||
|
||||
# Specialisation of AccessorData method
|
||||
class PrivateMethod(Method):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
from method_conv import *
|
||||
import private_method
|
||||
|
||||
# Specialisation of MethodConverter for AccessorData Methods
|
||||
class PrivateMethodConverter(MethodConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return private_method.PrivateMethod(cppfuncsig)
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
from func import *
|
||||
|
||||
# Specialisation of Function for global functions
|
||||
class StaticFunction(Function):
|
||||
def __init__(self, func_sig) -> None:
|
||||
super().__init__(func_sig)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
from func_conv import *
|
||||
import static_func
|
||||
|
||||
# Specialisation of FunctionConverter for global functions
|
||||
class StaticFunctionConverter(FunctionConverter):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def create_cpp_function(self, cppfuncsig):
|
||||
return static_func.StaticFunction(cppfuncsig)
|
||||
|
||||
def special_function_transforms(self, line):
|
||||
for cfuncsig, cppfuncsig in self._transforms.static_funcsigs.items():
|
||||
line,count = re.subn(rf"\b{cfuncsig.name}\s*\(", f"{cppfuncsig.name}(", line)
|
||||
if(count):
|
||||
debug.line("update_static_function_calls", f"name={cfuncsig.name} [after ]: {line}")
|
||||
|
||||
return super().special_function_transforms(line)
|
|
@ -0,0 +1,115 @@
|
|||
# C to C++ transforms, including arg, type, function name
|
||||
|
||||
import re
|
||||
import debug
|
||||
import arg
|
||||
from collections import namedtuple
|
||||
import copy
|
||||
|
||||
# Transforms are:
|
||||
#
|
||||
# types : {ctype, cpptype} type = "int", "GribStatus" etc
|
||||
# args : {carg, cpparg} arg = arg.Arg(type, name)
|
||||
# members : {cmember, cppmember} member = member.Member(type, name)
|
||||
# inherited_funcsigs : {cfuncsig, cppfuncsig} funcsig = func_sig.FuncSig(...)
|
||||
# private_funcsigs : {cfuncsig, cppfuncsig} funcsig = func_sig.FuncSig(...)
|
||||
# static_funcsigs : {cfuncsig, cppfuncsig} funcsig = func_sig.FuncSig(...)
|
||||
# class_types : {"Entry", {cvalue, cppvalue} } "Entry" is "self", "super", etc
|
||||
|
||||
# These entries are expected to exist!
|
||||
default_class_type_entries = ["self", "super"]
|
||||
|
||||
# Allow access of class_types tuples using .cvalue and .cppvalue for convenience!
|
||||
TypePair = namedtuple('TypePair', ['cvalue', 'cppvalue'])
|
||||
|
||||
class Transforms:
|
||||
def __init__(self, *, types={}, members={}, inherited_funcsigs={}, private_funcsigs={}, static_funcsigs={}) -> None:
|
||||
self._types = types
|
||||
self._all_args = {}
|
||||
self._global_args = {}
|
||||
self._members = members
|
||||
self._inherited_funcsigs = inherited_funcsigs
|
||||
self._private_funcsigs = private_funcsigs
|
||||
self._static_funcsigs = static_funcsigs
|
||||
self._class_types = {}
|
||||
|
||||
@property
|
||||
def types(self):
|
||||
return self._types
|
||||
|
||||
def add_to_types(self, ctype, cpptype):
|
||||
if ctype in self._types:
|
||||
assert self._types[ctype] == cpptype, f"Updating an existing type transform: {ctype} -> {cpptype} Previous type = {self._types[ctype]}"
|
||||
else:
|
||||
self._types[ctype] = cpptype
|
||||
debug.line("Transforms", f"Adding type transform: {ctype} -> {cpptype}")
|
||||
|
||||
@property
|
||||
def all_args(self):
|
||||
return self._all_args
|
||||
|
||||
def add_local_args(self, carg, cpparg):
|
||||
if carg in self._all_args:
|
||||
assert self._all_args[carg] == cpparg, f"Updating an existing local arg transform: C Arg = {arg.arg_string(carg)} -> {arg.arg_string(cpparg)} Previous arg = {arg.arg_string(self._all_args[carg])}"
|
||||
else:
|
||||
debug.line("Transforms", f"Adding new local arg transform: {arg.arg_string(carg)} -> {arg.arg_string(cpparg)}")
|
||||
self._all_args[carg] = cpparg
|
||||
|
||||
def clear_local_args(self):
|
||||
self._all_args.clear()
|
||||
self._all_args = copy.copy(self._global_args)
|
||||
|
||||
@property
|
||||
def global_args(self):
|
||||
return self._global_args
|
||||
|
||||
# Convert to a set of global transforms by moving the local transforms to the global area
|
||||
# and deleting the local transforms.
|
||||
# Call this after processing the global function to set up for all other function conversions
|
||||
def make_global(self):
|
||||
assert not self._global_args, "unexpected make_global call when global_args not empty"
|
||||
self._global_args = copy.deepcopy(self._all_args)
|
||||
self.clear_local_args()
|
||||
|
||||
@property
|
||||
def members(self):
|
||||
return self._members
|
||||
|
||||
def add_to_members(self, cmember, cppmember):
|
||||
if cmember in self._members:
|
||||
assert self._members[cmember] == cppmember, f"Updating an existing member transform: C member = {arg.arg_string(cmember)} -> {arg.arg_string(cppmember)} Previous member = {arg.arg_string(self._members[cmember])}"
|
||||
else:
|
||||
self._members[cmember] = cppmember
|
||||
|
||||
@property
|
||||
def inherited_funcsigs(self):
|
||||
return self._inherited_funcsigs
|
||||
|
||||
def add_to_inherited_funcsigs(self, cfuncsig, cppfuncsig):
|
||||
assert cfuncsig not in self._inherited_funcsigs, f"Setting an existing inherited_funcsig transform: {cfuncsig.name} -> {cppfuncsig.name}"
|
||||
self._inherited_funcsigs[cfuncsig] = cppfuncsig
|
||||
|
||||
@property
|
||||
def private_funcsigs(self):
|
||||
return self._private_funcsigs
|
||||
|
||||
def add_to_private_funcsigs(self, cfuncsig, cppfuncsig):
|
||||
assert cfuncsig not in self._private_funcsigs, f"Setting an existing private_funcsig transform: {cfuncsig.name} -> {cppfuncsig.name}"
|
||||
self._private_funcsigs[cfuncsig] = cppfuncsig
|
||||
|
||||
@property
|
||||
def static_funcsigs(self):
|
||||
return self._static_funcsigs
|
||||
|
||||
def add_to_static_funcsigs(self, cfuncsig, cppfuncsig):
|
||||
assert cfuncsig not in self._static_funcsigs, f"Setting an existing static_funcsig transform: {cfuncsig.name} -> {cppfuncsig.name}"
|
||||
self._static_funcsigs[cfuncsig] = cppfuncsig
|
||||
|
||||
@property
|
||||
def class_types(self):
|
||||
return self._class_types
|
||||
|
||||
def add_to_class_types(self, entry, cvalue, cppvalue):
|
||||
assert entry not in self._class_types, f"Updating an existing class type entry[{entry}] with cvalue={cvalue} cppvalue={cppvalue}"
|
||||
|
||||
self._class_types[entry] = TypePair(cvalue, cppvalue)
|
|
@ -1,106 +0,0 @@
|
|||
# C to C++ type transforms
|
||||
|
||||
import re
|
||||
from convert_debug import debug_line
|
||||
import arg_transforms as arg_transforms
|
||||
from arg_transforms import Arg
|
||||
|
||||
# Add any C to C++ type transforms required here
|
||||
# These are also added programmatically
|
||||
c_to_cpp_type_transforms = {
|
||||
"grib_accessor*" : None,
|
||||
"grib_handle*" : None,
|
||||
"grib_context*" : None,
|
||||
"char*" : "std::string",
|
||||
"char[]" : "std::string",
|
||||
"grib_iarray*" : "std::vector<long>"
|
||||
}
|
||||
|
||||
def add_c_to_cpp_type_transform(ctype, cpptype):
|
||||
c_to_cpp_type_transforms[ctype] = cpptype
|
||||
|
||||
# Tidy up type and arg, basically change int[10] val to int[] val
|
||||
def update_carg_format(carg):
|
||||
m = re.search(r"(\w*)(\[\d*\])", carg.type)
|
||||
if m:
|
||||
return Arg(m.group(1)+"[]", carg.name)
|
||||
|
||||
return carg
|
||||
|
||||
|
||||
# Returns the equivalent C++ arg (name and type), which could be None
|
||||
def to_cpp_arg(carg):
|
||||
|
||||
updated_carg = update_carg_format(carg)
|
||||
|
||||
# [1] Check for defined transforms
|
||||
for k, v in c_to_cpp_type_transforms.items():
|
||||
if k == updated_carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return Arg(v, arg_transforms.transform_variable_name(updated_carg.name))
|
||||
|
||||
# [2] Process any other array types
|
||||
m = re.match(r"(\w*)(\[\d*\])", updated_carg.type)
|
||||
if m:
|
||||
for k, v in c_to_cpp_type_transforms.items():
|
||||
if k == m.group(1):
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return Arg(f"std::vector<{v}>", arg_transforms.transform_variable_name(updated_carg.name))
|
||||
|
||||
return Arg(f"std::vector<{m.group(1)}>", arg_transforms.transform_variable_name(updated_carg.name))
|
||||
|
||||
# [3] Process other mapped types
|
||||
for k, v in c_to_cpp_type_transforms.items():
|
||||
if k == updated_carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return Arg(v, arg_transforms.transform_variable_name(updated_carg.name))
|
||||
|
||||
# [3] Return None for grib_accessor_*
|
||||
m = re.match(r"grib_accessor_", carg.type)
|
||||
if m:
|
||||
return None
|
||||
|
||||
# [4] Pointer types
|
||||
m = re.match(r"(\w*)\*", carg.type)
|
||||
if m:
|
||||
return Arg(f"std::vector<{m.group(1)}>", arg_transforms.transform_variable_name(updated_carg.name))
|
||||
|
||||
# [5] Everything else
|
||||
return Arg(carg.type, arg_transforms.transform_variable_name(carg.name))
|
||||
|
||||
# Returns the equivalent C++ arg (name and type), which could be None
|
||||
# The type is almost the same as for the function body, with the
|
||||
# following exceptions:
|
||||
# - pointers are converted to references (not std::vector)
|
||||
# - arrays are converted to std::vector references
|
||||
def to_cpp_func_sig_arg(carg):
|
||||
|
||||
# [1] Pointer types
|
||||
if carg.type[-1] == "*":
|
||||
# Check for defined transforms
|
||||
for k, v in c_to_cpp_type_transforms.items():
|
||||
if k == carg.type:
|
||||
if v is None:
|
||||
return None
|
||||
else:
|
||||
return Arg(v+"&", arg_transforms.transform_variable_name(carg.name))
|
||||
|
||||
# Other pointers: removing * to avoid getting std::vector type back
|
||||
cpp_arg = to_cpp_arg(Arg(carg.type[:-1], carg.name))
|
||||
|
||||
cpp_arg.type += "&"
|
||||
return cpp_arg
|
||||
|
||||
# [2] Everything else
|
||||
cpp_arg = to_cpp_arg(carg)
|
||||
|
||||
if cpp_arg and carg.type[-1] == "]":
|
||||
cpp_arg.type += "&"
|
||||
|
||||
return cpp_arg
|
Binary file not shown.
|
@ -89,6 +89,12 @@ bool AccessorData::isMissing() const
|
|||
return false;
|
||||
}
|
||||
|
||||
AccessorDataPtr AccessorData::clone() const
|
||||
{
|
||||
throw AccessorException(GribStatus::NOT_IMPLEMENTED); // TO DO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pack - single value
|
||||
GribStatus AccessorData::pack(long const& value)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,7 @@ public:
|
|||
virtual double nearestSmallerValue(double val) const;
|
||||
virtual bool compare(AccessorData const& rhs) const;
|
||||
virtual bool isMissing() const;
|
||||
virtual AccessorDataPtr clone() const;
|
||||
|
||||
// Pack - single value
|
||||
virtual GribStatus pack(long const& value);
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue