@@ -293,6 +293,7 @@ def standard_version(self) -> int:
293293 @property
294294 def has_variant (self ) -> bool :
295295 """
296+ Indicates if the language standard and flavor combo provides a type compliant with std::variant
296297 .. invisible-code-block: python
297298
298299 from nunavut.lang import LanguageClassLoader
@@ -329,7 +330,7 @@ def has_variant(self) -> bool:
329330
330331 assert language.has_variant
331332 """
332- return self .standard_version >= 17
333+ return ( self .standard_version >= 17 ) or ( self . standard_version == 14 and self . standard_flavor == "cetl" )
333334
334335 def get_includes (self , dep_types : Dependencies ) -> typing .List [str ]:
335336 """
@@ -361,6 +362,7 @@ def do_includes_test(override_vla_include, override_allocator_include, override_
361362
362363 test_dependencies = Dependencies()
363364 test_dependencies.uses_variable_length_array = True
365+ test_dependencies.uses_union = True
364366
365367 # If we override the include we should not provide the default
366368 # variable array include.
@@ -409,17 +411,16 @@ def do_includes_test(override_vla_include, override_allocator_include, override_
409411 std_includes .append ("array" )
410412 if dep_types .uses_boolean_static_array :
411413 std_includes .append ("bitset" )
412- if dep_types .uses_union and self .has_variant :
413- std_includes .append ("variant" )
414414 includes_formatted = [f"<{ include } >" for include in sorted (std_includes )]
415415
416416 allocator_include = str (self .get_option ("allocator_include" , "" ))
417417 if len (allocator_include ) > 0 :
418418 includes_formatted .append (allocator_include )
419419
420- variant_include = str (self .get_option ("variant_include" , "" ))
421- if len (variant_include ) > 0 :
422- includes_formatted .append (variant_include )
420+ if dep_types .uses_union :
421+ variant_include = str (self .get_option ("variant_include" , "" ))
422+ if len (variant_include ) > 0 :
423+ includes_formatted .append (variant_include )
423424
424425 if dep_types .uses_variable_length_array :
425426 variable_array_include = str (self .get_option ("variable_array_type_include" , "" ))
@@ -448,50 +449,65 @@ def create_vla_decl(self, data_type: str, max_size: int) -> str:
448449
449450
450451@template_language_test (__name__ )
451- def uses_std_variant (language : Language ) -> bool :
452+ def uses_variant (language : Language ) -> bool :
452453 """
453- Uses query for std variant.
454+ Uses query for variant.
454455
455- If the language options contain an ``std`` entry for C++ and the specified standard includes the
456- ``std::variant`` type added to the language at C++17 then this value is true. The logic included
457- in this filter can be stated as "options has key std and the value for options.std evaluates to
458- C++ version 17 or greater" but the implementation is able to parse out actual compiler flags like
459- ``gnu++20`` and is aware of any overrides to suppress use of the standard variant type even if
460- available.
456+ If the language options contain an entry for C++ and the specified standard or flavor includes a type that is
457+ compliant with ``std::variant``, then this value is true.
458+
459+ The logic included in this filter can be stated as:
460+ Options has keys [``std``, ``std_flavor``] and the values stored at those keys resolve to one of the following
461+ combinations:
462+ - {``std``: ``>="c++17"``}
463+ - {``std``: ``"c++14"``, ``std_flavor``: ``"cetl"``}
464+
465+ but the implementation is able to parse out actual compiler flags like ``gnu++20`` and is aware of any overrides
466+ to suppress use of the standard variant type even if available.
461467
462468 Example:
463469
464- .. code-block:: python
470+ .. code-block:: python
465471
466- template = '''
467- {%- ifuses "std_variant " -%}
468- #include <variant>
469- {%- else -%}
470- #include "user_variant.h"
471- {%- endifuses -%}
472- '''
472+ template = '''
473+ {%- ifuses "variant " -%}
474+ #include <variant>
475+ {%- else -%}
476+ #include "user_variant.h"
477+ {%- endifuses -%}
478+ '''
473479
474- .. invisible-code-block: python
480+ .. invisible-code-block: python
475481
476- # test c++17
477- options = {"std": "c++17"}
478- lctx = (
479- LanguageContextBuilder(include_experimental_languages=True)
480- .set_target_language("cpp")
481- .set_target_language_configuration_override(Language.WKCV_LANGUAGE_OPTIONS, options)
482- .create()
483- )
484- jinja_filter_tester(None, template, '#include <variant>', lctx)
482+ # test c++17
483+ options = {"std": "c++17"}
484+ lctx = (
485+ LanguageContextBuilder(include_experimental_languages=True)
486+ .set_target_language("cpp")
487+ .set_target_language_configuration_override(Language.WKCV_LANGUAGE_OPTIONS, options)
488+ .create()
489+ )
490+ jinja_filter_tester(None, template, '#include <variant>', lctx)
485491
486- # test c++14
487- options = {"std": "c++14"}
488- lctx = (
489- LanguageContextBuilder(include_experimental_languages=True)
490- .set_target_language("cpp")
491- .set_target_language_configuration_override(Language.WKCV_LANGUAGE_OPTIONS, options)
492- .create()
493- )
494- jinja_filter_tester(None, template, '#include "user_variant.h"', lctx)
492+ # test c++14-17 with CETL
493+ options = {"std": "c++14", "std_flavor": "cetl"}
494+ lctx = (
495+ LanguageContextBuilder(include_experimental_languages=True)
496+ .set_target_language("cpp")
497+ .set_target_language_configuration_override(Language.WKCV_LANGUAGE_OPTIONS, options)
498+ .create()
499+ )
500+ jinja_filter_tester(None, template, '#include <variant>', lctx)
501+
502+ # test c++14
503+ options = {"std": "c++14"}
504+ lctx = (
505+ LanguageContextBuilder(include_experimental_languages=True)
506+ .set_target_language("cpp")
507+ .set_target_language_configuration_override(Language.WKCV_LANGUAGE_OPTIONS, options)
508+ .create()
509+ )
510+ jinja_filter_tester(None, template, '#include "user_variant.h"', lctx)
495511
496512 """
497513 return language .has_variant
@@ -1057,8 +1073,8 @@ def filter_includes(language: Language, t: pydsdl.CompositeType, sort: bool = Tr
10571073 # Listing the includes for a union with only integer types:
10581074 template = "{% for include in my_type | includes -%}{{include}}{%- endfor %}"
10591075
1060- # cstdint will normally be generated. limits is always generated.
1061- rendered = "<cstdint><limits>"
1076+ # cstdint will normally be generated. limits and cetlpf are always generated.
1077+ rendered = "<cetl/pf17/cetlpf.hpp>< cstdint><limits>"
10621078
10631079 .. invisible-code-block: python
10641080
@@ -1068,7 +1084,7 @@ def filter_includes(language: Language, t: pydsdl.CompositeType, sort: bool = Tr
10681084
10691085 # You can suppress std includes by setting use_standard_types to False under
10701086 # nunavut.lang.cpp
1071- rendered = "<limits>"
1087+ rendered = "<cetl/pf17/cetlpf.hpp>< limits>"
10721088
10731089 .. invisible-code-block: python
10741090
@@ -1160,36 +1176,24 @@ def filter_default_value_initializer(language: Language, instance: pydsdl.Any) -
11601176 return ""
11611177
11621178
1163- def needs_initializing_value (special_method : SpecialMethod ) -> bool :
1164- """Helper method used by filter_value_initializer()"""
1165- return special_method == SpecialMethod .INITIALIZING_CONSTRUCTOR_WITH_ALLOCATOR or needs_rhs (special_method )
1166-
1167-
1168- def needs_rhs (special_method : SpecialMethod ) -> bool :
1169- """Helper method used by filter_value_initializer()"""
1170- return special_method in (
1171- SpecialMethod .COPY_CONSTRUCTOR_WITH_ALLOCATOR ,
1172- SpecialMethod .MOVE_CONSTRUCTOR_WITH_ALLOCATOR ,
1173- )
1174-
1175-
11761179def needs_allocator (instance : pydsdl .Any ) -> bool :
1177- """Helper method used by filter_value_initializer()"""
1178- return isinstance (instance .data_type , pydsdl .VariableLengthArrayType ) or isinstance (
1179- instance .data_type , pydsdl .CompositeType
1180- )
1180+ """
1181+ Return True if the provided instance requires an allocator on initialization, False if not
1182+ """
11811183
1184+ def type_needs_allocator (type : pydsdl .Any ) -> bool :
1185+ return isinstance (type , pydsdl .VariableLengthArrayType ) or isinstance (type , pydsdl .CompositeType )
11821186
1183- def needs_vla_init_args (instance : pydsdl .Any , special_method : SpecialMethod ) -> bool :
1184- """Helper method used by filter_value_initializer()"""
1185- return special_method == SpecialMethod .ALLOCATOR_CONSTRUCTOR and isinstance (
1186- instance .data_type , pydsdl .VariableLengthArrayType
1187- )
1187+ if hasattr (instance , "data_type" ):
1188+ return type_needs_allocator (instance .data_type )
1189+ return type_needs_allocator (instance )
11881190
11891191
1190- def needs_move (special_method : SpecialMethod ) -> bool :
1191- """Helper method used by filter_value_initializer()"""
1192- return special_method == SpecialMethod .MOVE_CONSTRUCTOR_WITH_ALLOCATOR
1192+ def filter_needs_allocator (instance : pydsdl .Any ) -> bool :
1193+ """
1194+ Check if the provided instance requires an allocator instance during initialization
1195+ """
1196+ return needs_allocator (instance )
11931197
11941198
11951199def requires_initialization (instance : pydsdl .Any ) -> bool :
@@ -1244,6 +1248,37 @@ def filter_value_initializer(
12441248 SpecialMethod.INITIALIZING_CONSTRUCTOR_WITH_ALLOCATOR)
12451249 assert '{foo}' == output
12461250
1251+ output = filter_value_initializer(
1252+ lctx.get_target_language(),
1253+ test_type,
1254+ SpecialMethod.COPY_CONSTRUCTOR_WITH_ALLOCATOR)
1255+ assert '{rhs.foo}' == output
1256+
1257+ output = filter_value_initializer(
1258+ lctx.get_target_language(),
1259+ test_type,
1260+ SpecialMethod.MOVE_CONSTRUCTOR_WITH_ALLOCATOR)
1261+ assert '{std::move(rhs.foo)}' == output
1262+
1263+ test_type.data_type = MagicMock(spec=pydsdl.CompositeType)
1264+
1265+ output = filter_value_initializer(
1266+ lctx.get_target_language(),
1267+ test_type,
1268+ SpecialMethod.INITIALIZING_CONSTRUCTOR_WITH_ALLOCATOR)
1269+ assert '{foo, allocator}' == output
1270+
1271+ output = filter_value_initializer(
1272+ lctx.get_target_language(),
1273+ test_type,
1274+ SpecialMethod.COPY_CONSTRUCTOR_WITH_ALLOCATOR)
1275+ assert '{rhs.foo, allocator}' == output
1276+
1277+ output = filter_value_initializer(
1278+ lctx.get_target_language(),
1279+ test_type,
1280+ SpecialMethod.MOVE_CONSTRUCTOR_WITH_ALLOCATOR)
1281+ assert '{std::move(rhs.foo), allocator}' == output
12471282 """
12481283
12491284 value_initializer : str = ""
@@ -1253,27 +1288,32 @@ def filter_value_initializer(
12531288 leading_args : typing .List [str ] = []
12541289 trailing_args : typing .List [str ] = []
12551290
1256- if needs_initializing_value (special_method ):
1257- instance_id = language .filter_id (instance )
1258- if needs_rhs (special_method ):
1259- rhs = f"rhs.{ instance_id } "
1260- else :
1261- rhs = f"{ id_prefix } { instance_id } "
1262-
1263- if needs_vla_init_args (instance , special_method ):
1264- constructor_args = language .get_option ("variable_array_type_constructor_args" )
1265- if isinstance (constructor_args , str ) and len (constructor_args ) > 0 :
1266- trailing_args .append (constructor_args .format (MAX_SIZE = instance .data_type .capacity ))
1291+ instance_id = language .filter_id (instance )
1292+
1293+ if special_method == SpecialMethod .ALLOCATOR_CONSTRUCTOR :
1294+ if isinstance (instance .data_type , pydsdl .ArrayType ):
1295+ if isinstance (instance .data_type , pydsdl .VariableLengthArrayType ):
1296+ constructor_args = language .get_option ("variable_array_type_constructor_args" )
1297+ if isinstance (constructor_args , str ) and len (constructor_args ) > 0 :
1298+ trailing_args .append (constructor_args .format (MAX_SIZE = instance .data_type .capacity ))
1299+ elif needs_allocator (instance .data_type .element_type ):
1300+ # Fixed length array with elements that require allocator on construction
1301+ element_init = str (filter_declaration (language , instance .data_type .element_type ) + "{allocator}" )
1302+ trailing_args .extend ([element_init ] * instance .data_type .capacity )
1303+ elif special_method == SpecialMethod .INITIALIZING_CONSTRUCTOR_WITH_ALLOCATOR :
1304+ rhs = f"{ id_prefix } { instance_id } "
1305+ else :
1306+ # COPY_CONSTRUCTOR_WITH_ALLOCATOR or MOVE_CONSTRUCTOR_WITH_ALLOCATOR
1307+ rhs = f"rhs.{ instance_id } "
1308+ if special_method == SpecialMethod .MOVE_CONSTRUCTOR_WITH_ALLOCATOR :
1309+ wrap = "std::move"
12671310
12681311 if needs_allocator (instance ):
12691312 if language .get_option ("ctor_convention" ) == ConstructorConvention .USES_LEADING_ALLOCATOR .value :
12701313 leading_args .extend (["std::allocator_arg" , "allocator" ])
12711314 else :
12721315 trailing_args .append ("allocator" )
12731316
1274- if needs_move (special_method ):
1275- wrap = "std::move"
1276-
12771317 value_initializer = assemble_initializer_expression (wrap , rhs , leading_args , trailing_args )
12781318
12791319 return value_initializer
0 commit comments