Added support for using Class::func

This commit is contained in:
kevstone 2024-02-07 23:00:10 +00:00
parent 7a575acd5a
commit ba803aa537
9 changed files with 122 additions and 14 deletions

View File

@ -3,6 +3,7 @@ import code_object.arg as arg
import code_object.code_interface as code_interface
import code_object.value_declaration_reference as value_declaration_reference
from utils.string_funcs import strip_semicolon
from code_object.code_interface import NONE_VALUE
# Represent a function call
#

View File

@ -8,6 +8,7 @@ from code_object.variable_declaration import VariableDeclaration
from code_object.array_access import ArrayAccess
from code_object.struct_member_access import StructMemberAccess
from code_object.value_declaration_reference import ValueDeclarationReference
from code_object.unary_operation import UnaryOperation
# Try to extract a name value from the object, else return ""
def extract_name(cpp_obj):
@ -28,6 +29,9 @@ def extract_name(cpp_obj):
return cpp_obj.name
elif isinstance(cpp_obj, ValueDeclarationReference):
return cpp_obj.value
elif isinstance(cpp_obj, UnaryOperation):
# Operand will a CodeInterface, so we need to recurse!
return extract_name(cpp_obj.operand)
return ""

View File

@ -5,11 +5,14 @@ import code_object_converter.conversion_pack.code_mappings as code_mappings
import code_object_converter.conversion_pack.funcsig_mapping as funcsig_mapping
import code_object_converter.conversion_pack.funcsig_pointer_mapping as funcsig_pointer_mapping
from code_object.arg import Arg
from code_object.function_call import FunctionCall
from code_object.funcsig import FuncSig
from code_object.data_member import DataMember
from code_object.declaration_specifier import DeclSpec
from code_object_converter.conversion_pack.conversion_data_helper import *
from code_object.code_interface import NONE_VALUE
import code_object_converter.conversion_pack.buffer_mapping as buffer_mapping
import code_object_converter.conversion_pack.arg_utils as arg_utils
from copy import deepcopy
# Store C to C++ conversion data to be used by the converters
@ -22,6 +25,9 @@ class ConversionData:
self._global_mappings = code_mappings.CodeMappings()
self._local_mappings = None
# The name of the c function currently being processed
self._current_cfuncname = ""
# Call this to ensure local state is set ready for function conversions
def set_local_state(self):
self._state = ConversionDataState.LOCAL
@ -30,7 +36,7 @@ class ConversionData:
# Clear out / reset local state data ready to convert a new function (cfuncname)
def reset_local_state(self, cfuncname):
self._local_mappings = code_mappings.CodeMappings()
self.info.current_cfuncname = cfuncname
self._current_cfuncname = cfuncname
debug.line("reset_local_state", f"cfuncname=[{cfuncname}]")
# The info object can be manipulated directly
@ -156,6 +162,26 @@ class ConversionData:
assert isinstance(type, str), f"Expected str, got [{type}]"
self.active_map.container_types.append(type)
# Converts the function call into a funcsig object (with void return type) and stores in the info
# object. This allows all arg type information to be available (for example when post-processing)
def add_cppfunction_call(self, cppfunction_call):
assert isinstance(cppfunction_call, FunctionCall), f"Expected FunctionCall, got [{cppfunction_call}]"
cppargs = []
for entry in cppfunction_call.args:
cppname = arg_utils.extract_name(entry)
cpparg = self.cpparg_for_cname(cppname) if cppname else None
debug.line("add_cppfunction_call", f"[{debug.as_debug_string(cppfunction_call)}]---> entry type=[{type(entry)}] value=[{debug.as_debug_string(entry)}] cppname=[{debug.as_debug_string(cppname)}] cpparg=[{debug.as_debug_string(cpparg)}]")
assert cppname
if cpparg and cpparg != NONE_VALUE:
cppargs.append(cpparg)
cppfuncsig = FuncSig("void", cppfunction_call.name, cppargs)
self._info.add_function_call_entry(cppfuncsig)
debug.line("add_cppfunction_call", f"Added function call cppfunction_call=[{debug.as_debug_string(cppfunction_call)}] -> cppfuncsig=[{debug.as_debug_string(cppfuncsig)}]")
# ============================== Functions to update the mappings: end ==============================
# ============================== Functions to query the mappings: start ==============================
@ -338,7 +364,7 @@ class ConversionData:
return None
def funcsig_mapping_for_current_cfuncname(self):
return self.funcsig_mapping_for_cfuncname(self._info.current_cfuncname)
return self.funcsig_mapping_for_cfuncname(self._current_cfuncname)
def funcsig_buffer_mapping_for_cname(self, cname):
for mapping in self.all_mappings():

View File

@ -35,10 +35,17 @@ class FunctionCallConverter(code_interface_converter.CodeInterfaceConverter):
for arg_entry in self._ccode_object.args:
cpp_arg_entry = conversion_funcs.convert_ccode_object(arg_entry, conversion_pack)
if cpp_arg_entry != NONE_VALUE:
if cpp_arg_entry and cpp_arg_entry != NONE_VALUE:
cpp_args.append(cpp_arg_entry)
cppfunction_call = function_call.FunctionCall(cpp_name, cpp_args)
# 3. Apply validation (and special handling)
return conversion_pack.conversion_validation.validate_function_call(cfunction_call, cppfunction_call)
updated_cppfunction_call = conversion_pack.conversion_validation.validate_function_call(cfunction_call, cppfunction_call)
# 4. Add the function call to the conversion data in case we need to process it later (e.g. for using C::x declarations)
# Only if function_call type!
if isinstance(updated_cppfunction_call, function_call.FunctionCall):
conversion_pack.conversion_data.add_cppfunction_call(updated_cppfunction_call)
return updated_cppfunction_call

View File

@ -1,7 +1,10 @@
import utils.debug as debug
from code_object.funcsig import FuncSig
# Stores info about the code, such as class name, includes etc
#
# NOTE: Any data stored here will be part of the final CppCode object
class CodeInfo:
def __init__(self, file_name, class_name, super_class_name="") -> None:
self._file_name = file_name
@ -9,11 +12,10 @@ class CodeInfo:
self._super_class_name = super_class_name
self._namespaces = []
self._forward_declarations = []
self._base_function_names = [] # i.e. for using ClassName::funcName entries
self._header_includes = []
self._source_includes = []
# Access directly to set/get the name of the c function currently being processed
self.current_cfuncname = ""
self._function_calls = [] # List all function calls made whilst converting (for post-processing)
@property
def file_name(self):
@ -35,6 +37,10 @@ class CodeInfo:
def forward_declarations(self):
return self._forward_declarations
@property
def base_function_names(self):
return self._base_function_names
@property
def header_includes(self):
return self._header_includes
@ -43,14 +49,31 @@ class CodeInfo:
def source_includes(self):
return self._source_includes
@property
def function_calls(self):
return self._function_calls
def add_namespace(self, entry):
self._namespaces.append(entry)
def add_forward_declaration(self, entry):
self._forward_declarations.append(entry)
def add_base_function_name(self, funcname):
if funcname not in self._base_function_names:
self._base_function_names.append(funcname)
def add_header_include(self, entry):
self._header_includes.append(entry)
def add_source_include(self, entry):
self._source_includes.append(entry)
def add_function_call_entry(self, entry):
assert isinstance(entry, FuncSig), f"add_function_call_entry requires FuncSig, not [{entry}]"
for func_call in self._function_calls:
if entry.name == func_call.name:
return # Duplicate
self._function_calls.append(entry)

View File

@ -39,6 +39,10 @@ class CppCode:
def forward_declarations(self):
return self._code_info.forward_declarations
@property
def base_function_names(self):
return self._code_info.base_function_names
@property
def header_file_includes(self):
return self._code_info.header_includes
@ -79,10 +83,6 @@ class CppCode:
def virtual_member_functions(self):
return self._code_elements.virtual_member_functions
@property
def virtual_member_functions_using_list(self):
return []
@property
def data_members(self):
return self._code_elements.data_members

View File

@ -35,6 +35,9 @@ class DefaultCCodeConverter:
self.convert_member_functions()
self.convert_virtual_member_functions()
# Post-processing
self.run_post_processing()
return cppcode.CppCode(self._code_info, self._code_elements)
# ============================== Setup functions: start ==============================
@ -111,6 +114,7 @@ class DefaultCCodeConverter:
if isinstance(cpp_func, member_function.MemberFunction):
cpp_func.class_name = self._conversion_pack.conversion_data.info.class_name
cpp_func.set_is_const(self.is_const_member_function(func.funcsig.name))
return cpp_func
def convert_functions(self):
@ -145,6 +149,18 @@ class DefaultCCodeConverter:
self._code_elements.add_virtual_member_function(virtual_member_func)
self.dump_function("convert_virtual_member_functions", virtual_member_func)
# ============================== Post-processing: begin ==============================
def run_post_processing(self):
self.post_process_function_calls()
# Override as required...
def post_process_function_calls(self):
pass
# ============================== Post-processing: end ==============================
# Helper for consistent debug output!
def dump_function(self, def_name, cppfunc):
debug.line("dump_function", "================================================================================")

View File

@ -21,6 +21,9 @@ import grib_accessor.supporting.arg_mappings as arg_mappings
import grib_accessor.supporting.data_member_mappings as data_member_mappings
import grib_accessor.grib_accessor_conversion_pack.grib_accessor_type_info as grib_accessor_type_info
import grib_accessor.grib_accessor_conversion_pack.grib_accessor_container_utils as grib_accessor_container_utils
import code_object_converter.conversion_pack.arg_utils as arg_utils
prefix = "grib_accessor_class_"
rename = {
"Gen": "Accessor", # "Generic",
@ -56,7 +59,6 @@ class GribAccessorCCodeConverter(default_ccode_converter.DefaultCCodeConverter):
return info
#
def function_specific_conversion_pack_updates(self, cfunction_name):
super().function_specific_conversion_pack_updates(cfunction_name)
# See if we have a function-specific validator,
@ -134,3 +136,32 @@ class GribAccessorCCodeConverter(default_ccode_converter.DefaultCCodeConverter):
def is_const_member_function(self, function_name):
return function_name in virtual_member_functions.const_virtual_member_function_names
def post_process_function_calls(self):
debug.line("post_process_function_calls", f"Function calls summary:")
for funcsig_call in self._code_info.function_calls:
debug.line("post_process_function_calls", f" -> [{debug.as_debug_string(funcsig_call)}]")
# See if this is a virtual function that isn't defined for this class, and add a using
# statement for it...
mismatch_found = False
for virt_func in self._code_elements.virtual_member_functions:
debug.line("post_process_function_calls", f"CHECKING VIRT_FUNC funcsig=[{debug.as_debug_string(virt_func.funcsig)}]")
if funcsig_call.name == virt_func.funcsig.name:
debug.line("post_process_function_calls", f"NAME MATCH -> funcsig_call=[{debug.as_debug_string(funcsig_call)}] virt_func.funcsig=[{debug.as_debug_string(virt_func.funcsig)}]")
# Get just the valid (not NONE_VALUE) args for the virtual function under test
virt_func_args = [a for a in virt_func.funcsig.args if a != NONE_VALUE]
for i in range(len(funcsig_call.args)):
if funcsig_call.args[i].decl_spec.type != virt_func_args[i].decl_spec.type:
# MISMATCH: Add base function name
debug.line("post_process_function_calls", f"MISMATCH FOUND, adding base function name=[{funcsig_call.name}]: i=[{i}] funcsig_call.args[i]=[{debug.as_debug_string(funcsig_call.args[i])}] virt_func_args[i]=[{debug.as_debug_string(virt_func_args[i])}]")
self._code_info.add_base_function_name(funcsig_call.name)
mismatch_found = True
break
if mismatch_found:
break

View File

@ -33,8 +33,8 @@ public:
protected:
// Virtual member functions
{%- for u in c.virtual_member_functions_using_list %}
using c.class_name::{{u}};{% endfor %}
{%- for u in c.base_function_names %}
using AccessorData::{{u}};{% endfor %}
{%- for m in c.virtual_member_functions %}
{{ m.funcsig_as_declaration }};{% endfor %}