nunavut.lang.cpp

Filters for generating C++. All filters in this module will be available in the template’s global namespace as cpp.

class nunavut.lang.cpp.Language(language_module: module, config: nunavut.lang._config.LanguageConfig, omit_serialization_support: bool, language_options: Optional[Mapping[str, Any]] = None)[source]

Bases: nunavut.lang.Language

Concrete, C++-specific nunavut.lang.Language object.

CPP_STD_EXTRACT_NUMBER_PATTERN = re.compile('(?:gnu|c)\\+\\+(\\d(?:\\w))')
get_includes(dep_types: nunavut.dependencies.Dependencies) → List[str][source]

Get a list of include paths that are specific to this language and the options set for it. :param Dependencies dep_types: A description of the dependencies includes are needed for. :return: A list of include file paths. The list may be empty if no includes were needed.

filter_id(instance: Any, id_type: str = 'any') → str[source]

Produces a valid identifier in the language for a given object. The encoding may not be reversible.

Parameters:
  • instance (any) – Any object or data that either has a name property or can be converted to a string.
  • id_type (str) – A type of identifier. This is different for each language. For example, for C this value can be ‘typedef’, ‘macro’, ‘function’, or ‘enum’. Use ‘any’ to apply stropping rules for all identifier types to the instance.
Returns:

A token that is a valid identifier in the language, is not a reserved keyword, and is transformed in a deterministic manner based on the provided instance.

nunavut.lang.cpp.uses_std_variant(language: nunavut.lang.cpp.Language) → bool[source]

Uses query for std variant.

If the language options contain an std entry for C++ and the specified standard includes the std::variant type added to the language at C++17 then this value is true. The logic included in this filter can be stated as “options has key std and the value for options.std evaluates to C++ version 17 or greater” but the implementation is able to parse out actual compiler flags like gnu++20 and is aware of any overrides to suppress use of the standard variant type even if available.

Example:

template = '''
    {%- ifuses "std_variant" -%}
        #include <variant>
    {%- else -%}
        #include "user_variant.h"
    {%- endifuses -%}
'''
nunavut.lang.cpp.filter_constant_value(language: nunavut.lang.cpp.Language, constant: pydsdl._serializable._attribute.Constant) → str[source]

Renders the specified value of the specified type as a literal.

nunavut.lang.cpp.filter_literal(language: nunavut.lang.cpp.Language, value: Union[fractions.Fraction, bool, int], ty: pydsdl._expression._any.Any, cast_format: Optional[str] = None) → str[source]

Renders the specified value of the specified type as a literal.

nunavut.lang.cpp.filter_to_standard_bit_length(t: pydsdl._serializable._primitive.PrimitiveType) → int[source]

Returns the nearest standard bit length of a type as an int.

nunavut.lang.cpp.filter_id(language: nunavut.lang.cpp.Language, instance: Any, id_type: str = 'any') → str[source]

Filter that produces a valid C and/or C++ identifier for a given object. The encoding may not be reversible.

# Given
I = 'I like c++'

# and
template = '{{ I | id }}'

# then
rendered = 'I_like_czX002BzX002B'
# Given
I = 'if'

# and
template = '{{ I | id }}'

# then
rendered = '_if'
# Given
I = 'I   really like     coffee'

# and
template = '{{ I | id }}'

# then
rendered = 'I_really_like_coffee'
Parameters:instance (any) – Any object or data that either has a name property or can be converted to a string.
Returns:A token that is a valid identifier for C and C++, is not a reserved keyword, and is transformed in a deterministic manner based on the provided instance.
nunavut.lang.cpp.filter_type(language: nunavut.lang.cpp.Language, obj: Any) → str[source]

Tries to convert a Python object into a c++ typename.

Will raise a ValueError if the object provided does not (yet) have an available conversion in this function.

Currently supported types are string:

 # given
template = '{{ "Any" | type }}'

# then
rendered = "const char* const"

int:

 # given
template = '{{ 123 | type }}'

# then
rendered = 'long long'

and bool:

 # given
template = '{{ True | type }}'

# then
rendered = 'bool'
nunavut.lang.cpp.filter_open_namespace(language: nunavut.lang.cpp.Language, full_namespace: str, bracket_on_next_line: bool = True, linesep: str = '\n') → str[source]

Emits c++ opening namespace syntax parsed from a pydsdl “full_namespace”, dot-separated value.

# Given
T.full_namespace = 'uavcan.foo'

# and
template = '{{ T.full_namespace | open_namespace }}'

# then
rendered = '''namespace uavcan
{
namespace foo
{'''
Parameters:
  • full_namespace (str) – A dot-separated namespace string.
  • bracket_on_next_line (bool) – If True (the default) then the opening brackets are placed on a newline after the namespace keyword.
  • linesep (str) – The line-separator to use when emitting new lines. By default this is \n.
Returns:

C++ namespace declarations with opening brackets.

nunavut.lang.cpp.filter_close_namespace(language: nunavut.lang.cpp.Language, full_namespace: str, omit_comments: bool = False, linesep: str = '\n') → str[source]

Emits c++ closing namespace syntax parsed from a pydsdl “full_namespace”, dot-separated value.

# Given
T.full_namespace = 'uavcan.foo'

# and
template = '{{ T.full_namespace | close_namespace }}'

# then
rendered = '''} // namespace foo
} // namespace uavcan'''
Parameters:
  • full_namespace (str) – A dot-separated namespace string.
  • omit_comments (bool) – If True then the comments following the closing bracket are omitted.
  • linesep (str) – The line-separator to use when emitting new lines. By default this is \n
Returns:

C++ namespace declarations with opening brackets.

nunavut.lang.cpp.filter_full_reference_name(language: nunavut.lang.cpp.Language, t: pydsdl._serializable._composite.CompositeType) → str[source]

Provides a string that is the full namespace, typename, major, and minor version for a given composite type.

# Given a type with illegal characters for C++
my_obj.full_name = 'any.int.2Foo'
my_obj.version.major = 1
my_obj.version.minor = 2

# and
template = '{{ my_obj | full_reference_name }}'

# then, with stropping enabled
rendered = 'any::_int::_2Foo_1_2'
Parameters:t (pydsdl.CompositeType) – The DSDL type to get the fully-resolved reference name for.
nunavut.lang.cpp.filter_full_macro_name(language: nunavut.lang.cpp.Language, t: pydsdl._serializable._composite.CompositeType) → str[source]

Provides a string usable as part of a macro name that is the full namespace, typename, major, and minor version for a given composite type.

# Given a type with illegal characters for C++
my_obj.full_name = 'any.int.2Foo'
my_obj.version.major = 1
my_obj.version.minor = 2

# and
template = '{{ my_obj | full_macro_name }}'

# then, with stropping enabled
rendered = 'any__int__2Foo_1_2'
Parameters:t (pydsdl.CompositeType) – The DSDL type to get the fully-resolved reference name for.
nunavut.lang.cpp.filter_short_reference_name(language: nunavut.lang.cpp.Language, t: pydsdl._serializable._composite.CompositeType) → str[source]

Provides a string that is a shorted version of the full reference name. This type is unique only within its namespace.

# Given a type with illegal C++ characters
my_type.short_name = '2Foo'
my_type.version.major = 1
my_type.version.minor = 2

# and
template = '{{ my_type | short_reference_name }}'

# then, with stropping enabled
rendered = '_2Foo_1_2'
# Given a type with legal C++ characters
my_type.short_name = 'Struct_'
my_type.version.major = 0
my_type.version.minor = 1

# and
template = '{{ my_type | short_reference_name }}'

# then, with stropping enabled
rendered = 'Struct__0_1'
# Given a service type
my_service_type.short_name = 'Struct_'
my_service_type.version.major = 0
my_service_type.version.minor = 1

# and
template = '''
{{ my_service_type | short_reference_name }}
{{ my_service_type.request_type | short_reference_name }}
{{ my_service_type.response_type | short_reference_name }}
'''

# then, with stropping enabled
rendered = '''
Struct_
Request_0_1
Response_0_1
'''
Parameters:t (pydsdl.CompositeType) – The DSDL type to get the reference name for.
nunavut.lang.cpp.filter_includes(language: nunavut.lang.cpp.Language, t: pydsdl._serializable._composite.CompositeType, sort: bool = True) → List[str][source]

Returns a list of all include paths for a given type.

Parameters:
  • t (pydsdl.CompositeType) – The type to scan for dependencies.
  • sort (bool) – If true the returned list will be sorted.
Returns:

a list of include headers needed for a given type.

nunavut.lang.cpp.filter_destructor_name(language: nunavut.lang.cpp.Language, instance: pydsdl._expression._any.Any) → str[source]

Returns a token that is the local destructor name. For example:

# Given a pydsdl.FixedLengthArrayType "my_type":
my_type.short_name = 'Foo'
my_type.capacity = 2

# and
template = 'ptr->{{ my_type | destructor_name }}'

# then
rendered = 'ptr->~array<std::uint8_t,2>'
Parameters:t (pydsdl.CompositeType) – The type to generate a destructor template for.
Returns:A destructor name token.
nunavut.lang.cpp.filter_declaration(language: nunavut.lang.cpp.Language, instance: pydsdl._expression._any.Any) → str[source]

Emit a declaration statement for the given instance.

nunavut.lang.cpp.filter_definition_begin(language: nunavut.lang.cpp.Language, instance: pydsdl._serializable._composite.CompositeType) → str[source]

Emit the start of a definition statement for a composite type.

# Given a pydsdl.CompositeType "my_type":
my_type.short_name = 'Foo'
my_type.version.major = 1
my_type.version.minor = 0

# and
template = '{{ my_type | definition_begin }}'

# then
rendered = 'struct Foo_1_0'
# Also, given a pydsdl.UnionType "my_union_type":
my_union_type.short_name = 'Foo'
my_union_type.version.major = 1
my_union_type.version.minor = 0

# and
union_template = '{{ my_union_type | definition_begin }}'

# then
rendered = 'struct Foo_1_0'
# Finally, given a pydsdl.ServiceType "my_service_type":
my_service_type.short_name = 'Foo'
my_service_type.version.major = 1
my_service_type.version.minor = 0

# and
template = '''
{{ my_service_type | definition_begin }};
{{ my_service_type.request_type | definition_begin }};
{{ my_service_type.response_type | definition_begin }};
'''

# then
rendered = '''
namespace Foo;
struct Request_1_0;
struct Response_1_0;
'''
nunavut.lang.cpp.filter_definition_end(language: nunavut.lang.cpp.Language, instance: pydsdl._serializable._composite.CompositeType) → str[source]

Emit the end of a definition statement for a composite type.

nunavut.lang.cpp.filter_type_from_primitive(language: nunavut.lang.cpp.Language, value: pydsdl._serializable._primitive.PrimitiveType) → str[source]

Filter to transform a pydsdl PrimitiveType into a valid C++ type.

# Given
template = '{{ unsigned_int_32_type | type_from_primitive }}'

# then
rendered = 'std::uint32_t'

Also note that this is sensitive to the use_standard_types configuration in the language properties:

# rendered will be different if use_standard_types is False
rendered = 'unsigned long'
Parameters:value (str) – The dsdl primitive to transform.
Returns:A valid C++ type name.
Raises:TemplateRuntimeError – If the primitive cannot be represented as a standard C++ type.
nunavut.lang.cpp.filter_to_namespace_qualifier(namespace_list: List[str]) → str[source]

Converts a list of namespace names into a qualifier string. For example:

my_namespace = ['foo', 'bar']
template = '{{ my_namespace | to_namespace_qualifier }}myType()'
expected = 'foo::bar::myType()'

This filter gracefully handles empty namespace lists:

my_namespace = []
template = '{{ my_namespace | to_namespace_qualifier }}myType()'
expected = 'myType()'
nunavut.lang.cpp.filter_to_template_unique_name(base_token: str) → str[source]

Filter that takes a base token and forms a name that is very likely to be unique within the template the filter is invoked. This name is also very likely to be a valid C++ identifier.

Important

The exact tokens generated may change between major or minor versions of this library. The only guarantee provided is that the tokens will be stable for the same version of this library given the same input.

Also note that name uniqueness is only likely within a given template. Between templates there is no guarantee of uniqueness and, since this library does not lex generated source, there is no guarantee that the generated name does not conflict with a name generated by another means.

# Given
template  = '{{ "foo" | to_template_unique_name }},{{ "Foo" | to_template_unique_name }},'
template += '{{ "fOO" | to_template_unique_name }}'

# then
rendered = '_foo0_,_foo1_,_fOO0_'
# Given
template = '{{ "i like coffee" | to_template_unique_name }}'

# then
rendered = '_i like coffee0_'
Parameters:base_token (str) – A token to include in the base name.
Returns:A name that is likely to be valid C++ identifier and is likely to be unique within the file generated by the current template.
nunavut.lang.cpp.filter_as_boolean_value(value: bool) → str[source]

Filter a boolean expression to produce a valid C++ “true” or “false” token.

assert "true" == filter_as_boolean_value(True)
assert "false" == filter_as_boolean_value(False)
nunavut.lang.cpp.filter_indent_if_not(language: nunavut.lang.cpp.Language, text: str, depth: int = 1) → str[source]

Emit indent characters as configured for the language but only as needed. This is different from the built-in indent filter in that it may add or remove spaces based on the existing indent.

# Given a string with an existing indent of 4 spaces...
template  = '{{ "    int a = 1;" | indent_if_not }}'

# then if cpp.indent == 4 we expect no change.
rendered = '    int a = 1;'
# If the indent is only 3 spaces...
template  = '{{ "   int a = 1;" | indent_if_not }}'

# then if cpp.indent == 4 we expect 4 spaces (i.e. not 7 spaces)
rendered = '    int a = 1;'
# We can also specify multiple indents...
template  = '{{ "int a = 1;" | indent_if_not(2) }}'

rendered = '        int a = 1;'
# ...or no indent
template  = '{{ "    int a = 1;" | indent_if_not(0) }}'

rendered = 'int a = 1;'
# Finally, note that blank lines are not indented.
template  = '''
    {%- set block_text -%}
        int a = 1;
        {# empty line #}
        int b = 2;
    {%- endset -%}{{ block_text | indent_if_not(1) }}'''

rendered  = '    int a = 1;'
rendered += '\n'
rendered += '\n'  # Nothing but spaces so this is stripped
rendered += '    int b = 2;'
Parameters:
  • text – The text to indent.
  • depth – The number of indents. For example, if depth is 2 and the indent for this language is 4 spaces then the text will be indented by 8 spaces.
nunavut.lang.cpp.filter_minimum_required_capacity_bits(t: pydsdl._serializable._serializable.SerializableType) → int[source]

Returns the minimum number of bits required to store the deserialized value of a pydsdl SerializableType. This capacity may be too small for some instances of the value (e.g. variable length arrays).

# Given
template = '{{ unsigned_int_32_type | minimum_required_capacity_bits }}'

# then
rendered = '32'
Parameters:t (pydsdl.SerializableType) – The dsdl type.
Returns:The minimum, required bits needed to store some values of the given type.
nunavut.lang.cpp.filter_block_comment(language: nunavut.lang.cpp.Language, text: str, style: str, indent: int = 0, line_length: int = 100) → str[source]

Reformats text as a block comment using Python’s textwrap.TextWrapper.wrap() function.

Parameters:
  • text – The text to emit as a block comment.
  • style – Dictates the style of comments (see return documentation for valid style names).
  • indent – The number of spaces to indent the comments by (tab indent is not supported. Sorry).
  • line_length – The soft maximum width to wrap text at. Some violations may occur where long words are used.
Returns str:

A comment block. Comment styles supported are:

javadoc

# Given a type with the following docstring
text = 'This is a bunch of documentation.'

# and
template = '''
    {{ text | block_comment('javadoc', 4, 24) }}
    void some_method();
'''

# the output will be
rendered = '''
    /**
     * This is a bunch
     * of documentation.
     */
    void some_method();
'''

cpp-doxygen

# that same template using the cpp style of doxygen...
template = '''
    {{ text | block_comment('cpp-doxygen', 4, 24) }}
    void some_method();
'''

# ...will be
rendered = '''
    ///
    /// This is a bunch
    /// of
    /// documentation.
    ///
    void some_method();
'''

cpp

# also supported is cpp style...
template = '''
    {{ text | block_comment('cpp', 4, 24) }}
    void some_method();
'''

rendered = '''
    // This is a bunch of
    // documentation.
    void some_method();
'''

c

# c style...
template = '''
    {{ text | block_comment('c', 4, 24) }}
    void some_method();
'''

rendered = '''
    /*
     * This is a bunch
     * of documentation.
     */
    void some_method();
'''

qt

# and Qt style...
template = '''
    {{ text | block_comment('qt', 4, 24) }}
    void some_method();
'''

rendered = '''
    /*!
     * This is a bunch
     * of documentation.
     */
    void some_method();
'''