Simple C++ code transformations#
Visiting the CppElement tree#
Let’s transform the following code into a tree of CppElement
:
code = """
int AddNumbers(const MyModule_Class& c);
"""
import srcmlcpp
options = srcmlcpp.SrcmlcppOptions()
cpp_unit = srcmlcpp.code_to_cpp_unit(options, code)
We can then “visit” this tree, and for example log the type of all encountered elements:
def visitor_log_info(cpp_element: srcmlcpp.CppElement, event: srcmlcpp.CppElementsVisitorEvent, depth: int) -> None:
if event == srcmlcpp.CppElementsVisitorEvent.OnElement:
print(" " * depth + cpp_element.short_cpp_element_info())
cpp_unit.visit_cpp_breadth_first(visitor_log_info)
CppUnit
CppEmptyLine
CppFunctionDecl name=AddNumbers
CppType name=int
CppParameterList
CppParameter
CppDecl name=c
CppType name=MyModule_Class
CppEmptyLine
Transforming the CppElement tree#
Let’s suppose we want to apply a mass source code transformation where:
all functions names should be transformed to “snake_case” (with a warning on top)
all types whose name start with
MyModule_
should be replaced byMyModule::
(i.e. we want to add a namespace)
In our example,
int AddNumbers(const MyModule_Class& c);
should be transformed to
// Was changed to snake_case!
int add_numbers(const MyModule::Class& c);
We can achieve this by defining my_refactor_visitor
this way:
from srcmlcpp import CppFunctionDecl, CppType # import the types we want to apply transformations to
from codemanip import code_utils # for to_snake_case
def change_function_to_snake_case(cpp_function: CppFunctionDecl):
"""Change a function name to snake_case"""
cpp_function.function_name = code_utils.to_snake_case(cpp_function.function_name)
cpp_function.cpp_element_comments.comment_on_previous_lines += "Was changed to snake case!"
def make_my_module_namespace(cpp_type: CppType):
"""If a type starts with MyModule_, replace it by MyModule::"""
def change_typename(typename: str):
if typename.startswith("MyModule_"):
return typename.replace("MyModule_", "MyModule::")
else:
return typename
cpp_type.typenames = [change_typename(typename) for typename in cpp_type.typenames]
def my_refactor_visitor(cpp_element: srcmlcpp.CppElement, event: srcmlcpp.CppElementsVisitorEvent, depth: int) -> None:
"""Our visitor will apply the transformation"""
if event == srcmlcpp.CppElementsVisitorEvent.OnElement:
if isinstance(cpp_element, CppFunctionDecl):
change_function_to_snake_case(cpp_element)
elif isinstance(cpp_element, CppType):
make_my_module_namespace(cpp_element)
Let’s run this visitor and see its output:
# Let's visit the code
cpp_unit.visit_cpp_breadth_first(my_refactor_visitor)
# And print the refactored code
from litgen.demo import litgen_demo
litgen_demo.show_cpp_code(cpp_unit.str_code())
//Was changed to snake case!
int add_numbers(const MyModule::Class & c);
Note: str_code_verbatim
still retains the original source code
litgen_demo.show_cpp_code(cpp_unit.str_code_verbatim())
int AddNumbers(const MyModule_Class& c);