Filtering header content¶
A C/C++ header can contains different zone, some of which are parts of the public API, and some of which may correspond to specific low-level options.
litgen (and srcmlcpp) can filter a header based on preprocessor #ifdef, #ifndef and #if occurrences.
For #if, the macro name referenced in the condition is matched against header_filter_acceptable__regex, exactly like #ifdef: both #if MY_OPTION and #if defined(MY_OPTION) are filtered on the name MY_OPTION (this is a name match, not an evaluation of the macro’s value).
Within an accepted region, only the primary branch is kept: the content of #else and #elif branches is dropped (their #else / #elif / #endif markers themselves remain).
Let’s look at an example header: its code is defined in the cpp_code variable below.
cpp_code = """
#ifndef MY_HEADER_H // This is an inclusion guard, it should not be filtered out
void Foo() {}
#ifdef ARCANE_OPTION
// We are entering a zone that handle arcane options that should not be included in the bindings
void Foo2() {}
#else
// this should also not be included in the bindings
void Foo3() {}
#endif
#ifdef COMMON_OPTION
// We are entering a zone for which we would like to publish bindings
void Foo4();
#else
// The #else branch of an accepted region is dropped: only the primary branch is kept
void Foo4Fallback();
#endif
#if defined(COMMON_OPTION)
// `#if` is filtered like `#ifdef`: this is also published once COMMON_OPTION is acceptable
void Foo5();
#endif
#endif // #ifndef MY_HEADER_H
"""Let’s try to generate bindings for it:
import litgen
from litgen.demo import litgen_demo
options = litgen.LitgenOptions()
litgen_demo.demo(options, cpp_code)We see that elements inside #ifdef ARCANE_OPTION were not processed (as desired). However, elements inside #ifdef COMMON_OPTION and #if defined(COMMON_OPTION) were also not processed, even though we would like to publish them. Let’s correct this by adjusting the options:
options = litgen.LitgenOptions()
# the default value for header_filter_acceptable__regex was
# "__cplusplus|_h_$|_h$|_H$|_H_$|hpp$|HPP$|hxx$|HXX$"
# (which includes support for common header guards)
# Let's add support for COMMON_OPTION
options.srcmlcpp_options.header_filter_acceptable__regex = "_H$|^COMMON_OPTION$"
litgen_demo.demo(options, cpp_code)API Prefixes¶
In a given header files, function can have an “API Prefix”, that denotes whether they should be published or not in a shared library.
cpp_code = """
// This function has an API marker and is exported in a shared library
MY_API int add(int, int b);
// This function does not have an API marker, and is probably private
int mul(int a, int b);
"""You can set the API marker in the options:
options = litgen.LitgenOptions()
options.srcmlcpp_options.functions_api_prefixes = "MY_API"
litgen_demo.demo(options, cpp_code)You can also decide to export non API function, with an optional comment.
options = litgen.LitgenOptions()
options.srcmlcpp_options.functions_api_prefixes = "MY_API"
options.fn_exclude_non_api = False
options.fn_non_api_comment = "Private API!"
litgen_demo.demo(options, cpp_code)Header preprocessing¶
If you need to preprocess header code before the generation, you can create a function that transforms the source code, and store it inside options.srcmlcpp_options.code_preprocess_function.
For example:
def preprocess_change_int(code: str) -> str:
return code.replace("int", "Int32") # This is a *very* dumb preprocessor
cpp_code = """
int add(int, int b);
"""
options = litgen.LitgenOptions()
options.srcmlcpp_options.code_preprocess_function = preprocess_change_int
generated_code = litgen.generate_code(options, cpp_code)
litgen_demo.show_cpp_code(generated_code.stub_code)