Refactoring to detect forward declarations

This commit is contained in:
kevstone 2023-10-11 20:05:05 +01:00
parent 10077ae7e1
commit 527a289e66
41 changed files with 3593 additions and 1546 deletions

125
src/conversion/accessor_data.py Executable file
View File

@ -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)

View File

@ -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:]

128
src/conversion/arg_conv.py Executable file
View File

@ -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:]

View File

@ -1,3 +1,4 @@
# Convert C definitions (e.g. to enums)
GribStatusConverter = {
"GRIB_SUCCESS": "GribStatus::SUCCESS",

View File

@ -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

View File

@ -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])

View File

@ -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

1516
src/conversion/convert_ORIG.py Executable file

File diff suppressed because it is too large Load Diff

View File

@ -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}")

57
src/conversion/debug.py Executable file
View File

@ -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}")

View File

@ -0,0 +1,8 @@
from method import *
# Specialisation of AccessorData method
class DestructorMethod(Method):
def __init__(self, func_sig) -> None:
super().__init__(func_sig)

View File

@ -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)

62
src/conversion/func.py Executable file
View File

@ -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)

490
src/conversion/func_conv.py Executable file
View File

@ -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

68
src/conversion/funcsig.py Executable file
View File

@ -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])})"

12
src/conversion/global_func.py Executable file
View File

@ -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

View File

@ -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)

137
src/conversion/grib_accessor.py Executable file
View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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)

View File

@ -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 %}

View File

@ -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 }}

View File

@ -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 %}

View File

@ -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 }}

7
src/conversion/member.py Executable file
View File

@ -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

39
src/conversion/member_conv.py Executable file
View File

@ -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"

View File

@ -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

19
src/conversion/method.py Executable file
View File

@ -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

127
src/conversion/method_conv.py Executable file
View File

@ -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)

View File

@ -0,0 +1,8 @@
from method import *
# Specialisation of AccessorData method
class PrivateMethod(Method):
def __init__(self, func_sig) -> None:
super().__init__(func_sig)

View File

@ -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)

8
src/conversion/static_func.py Executable file
View File

@ -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)

View File

@ -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)

115
src/conversion/transforms.py Executable file
View File

@ -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)

View File

@ -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

BIN
src/convert.py.lprof Normal file

Binary file not shown.

View File

@ -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)
{

View File

@ -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);

BIN
src/output.prof Normal file

Binary file not shown.