import os
from builtins import range
from functools import reduce
from typing import Any, Dict, List
import functools
import json
def get_libcxx_paths():
utils_path = os.path.dirname(os.path.abspath(__file__))
script_name = os.path.basename(__file__)
assert os.path.exists(utils_path)
src_root = os.path.dirname(utils_path)
include_path = os.path.join(src_root, "include")
assert os.path.exists(include_path)
docs_path = os.path.join(src_root, "docs")
assert os.path.exists(docs_path)
macro_test_path = os.path.join(
src_root,
"test",
"std",
"language.support",
"support.limits",
"support.limits.general",
)
assert os.path.exists(macro_test_path)
assert os.path.exists(
os.path.join(macro_test_path, "version.version.compile.pass.cpp")
)
return script_name, src_root, include_path, docs_path, macro_test_path
script_name, source_root, include_path, docs_path, macro_test_path = get_libcxx_paths()
def has_header(h):
h_path = os.path.join(include_path, h)
return os.path.exists(h_path)
def add_version_header(tc):
tc["headers"].append("version")
return tc
feature_test_macros = [
add_version_header(x)
for x in [
{
"name": "__cpp_lib_adaptor_iterator_pair_constructor",
"values": {"c++23": 202106},
"headers": ["queue", "stack"],
},
{
"name": "__cpp_lib_addressof_constexpr",
"values": {"c++17": 201603},
"headers": ["memory"],
},
{
"name": "__cpp_lib_allocate_at_least",
"values": {
"c++23": 202302,
},
"headers": ["memory"],
},
{
"name": "__cpp_lib_allocator_traits_is_always_equal",
"values": {"c++17": 201411},
"headers": [
"deque",
"forward_list",
"list",
"map",
"memory",
"scoped_allocator",
"set",
"string",
"unordered_map",
"unordered_set",
"vector",
],
},
{
"name": "__cpp_lib_any",
"values": {"c++17": 201606},
"headers": ["any"],
},
{
"name": "__cpp_lib_apply",
"values": {"c++17": 201603},
"headers": ["tuple"],
},
{
"name": "__cpp_lib_array_constexpr",
"values": {"c++17": 201603, "c++20": 201811},
"headers": ["array", "iterator"],
},
{
"name": "__cpp_lib_as_const",
"values": {"c++17": 201510},
"headers": ["utility"],
},
{
"name": "__cpp_lib_associative_heterogeneous_erasure",
"values": {"c++23": 202110},
"headers": ["map", "set", "unordered_map", "unordered_set"],
"unimplemented": True,
},
{
"name": "__cpp_lib_associative_heterogeneous_insertion",
"values": {
"c++26": 202306
},
"headers": ["map", "set", "unordered_map", "unordered_set"],
"unimplemented": True,
},
{
"name": "__cpp_lib_assume_aligned",
"values": {"c++20": 201811},
"headers": ["memory"],
},
{
"name": "__cpp_lib_atomic_flag_test",
"values": {"c++20": 201907},
"headers": ["atomic"],
},
{
"name": "__cpp_lib_atomic_float",
"values": {"c++20": 201711},
"headers": ["atomic"],
"unimplemented": True,
},
{
"name": "__cpp_lib_atomic_is_always_lock_free",
"values": {"c++17": 201603},
"headers": ["atomic"],
},
{
"name": "__cpp_lib_atomic_lock_free_type_aliases",
"values": {"c++20": 201907},
"headers": ["atomic"],
},
{
"name": "__cpp_lib_atomic_min_max",
"values": {"c++26": 202403},
"headers": ["atomic"],
"unimplemented": True,
},
{
"name": "__cpp_lib_atomic_ref",
"values": {"c++20": 201806},
"headers": ["atomic"],
},
{
"name": "__cpp_lib_atomic_shared_ptr",
"values": {"c++20": 201711},
"headers": ["atomic"],
"unimplemented": True,
},
{
"name": "__cpp_lib_atomic_value_initialization",
"values": {"c++20": 201911},
"headers": ["atomic", "memory"],
},
{
"name": "__cpp_lib_atomic_wait",
"values": {"c++20": 201907},
"headers": ["atomic"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC",
"libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_SYNC",
},
{
"name": "__cpp_lib_barrier",
"values": {"c++20": 201907},
"headers": ["barrier"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
},
{
"name": "__cpp_lib_bind_back",
"values": {
"c++23": 202202,
},
"headers": ["functional"],
},
{
"name": "__cpp_lib_bind_front",
"values": {
"c++20": 201907,
"c++26": 202306,
},
"headers": ["functional"],
},
{
"name": "__cpp_lib_bit_cast",
"values": {"c++20": 201806},
"headers": ["bit"],
},
{
"name": "__cpp_lib_bitops",
"values": {"c++20": 201907},
"headers": ["bit"],
},
{
"name": "__cpp_lib_bitset",
"values": {"c++26": 202306},
"headers": ["bitset"],
},
{
"name": "__cpp_lib_bool_constant",
"values": {"c++17": 201505},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_bounded_array_traits",
"values": {"c++20": 201902},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_boyer_moore_searcher",
"values": {"c++17": 201603},
"headers": ["functional"],
},
{
"name": "__cpp_lib_byte",
"values": {"c++17": 201603},
"headers": ["cstddef"],
},
{
"name": "__cpp_lib_byteswap",
"values": {"c++23": 202110},
"headers": ["bit"],
},
{
"name": "__cpp_lib_char8_t",
"values": {"c++20": 201907},
"headers": [
"atomic",
"filesystem",
"istream",
"limits",
"locale",
"ostream",
"string",
"string_view",
],
"test_suite_guard": "defined(__cpp_char8_t)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_CHAR8_T)",
},
{
"name": "__cpp_lib_chrono",
"values": {
"c++17": 201611,
},
"headers": ["chrono"],
},
{
"name": "__cpp_lib_chrono_udls",
"values": {"c++14": 201304},
"headers": ["chrono"],
},
{
"name": "__cpp_lib_clamp",
"values": {"c++17": 201603},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_complex_udls",
"values": {"c++14": 201309},
"headers": ["complex"],
},
{
"name": "__cpp_lib_concepts",
"values": {"c++20": 202002},
"headers": ["concepts"],
},
{
"name": "__cpp_lib_constexpr_algorithms",
"values": {
"c++20": 201806,
},
"headers": ["algorithm", "utility"],
},
{
"name": "__cpp_lib_constexpr_bitset",
"values": {"c++23": 202207},
"headers": ["bitset"],
},
{
"name": "__cpp_lib_constexpr_charconv",
"values": {"c++23": 202207},
"headers": ["charconv"],
},
{
"name": "__cpp_lib_constexpr_cmath",
"values": {"c++23": 202202},
"headers": ["cmath", "cstdlib"],
"unimplemented": True,
},
{
"name": "__cpp_lib_constexpr_complex",
"values": {"c++20": 201711},
"headers": ["complex"],
},
{
"name": "__cpp_lib_constexpr_dynamic_alloc",
"values": {"c++20": 201907},
"headers": ["memory"],
},
{
"name": "__cpp_lib_constexpr_functional",
"values": {"c++20": 201907},
"headers": ["functional"],
},
{
"name": "__cpp_lib_constexpr_iterator",
"values": {"c++20": 201811},
"headers": ["iterator"],
},
{
"name": "__cpp_lib_constexpr_memory",
"values": {"c++20": 201811, "c++23": 202202},
"headers": ["memory"],
},
{
"name": "__cpp_lib_constexpr_new",
"values": {"c++26": 202406},
"headers": ["new"],
"unimplemented": True,
},
{
"name": "__cpp_lib_constexpr_numeric",
"values": {"c++20": 201911},
"headers": ["numeric"],
},
{
"name": "__cpp_lib_constexpr_string",
"values": {"c++20": 201907},
"headers": ["string"],
},
{
"name": "__cpp_lib_constexpr_string_view",
"values": {"c++20": 201811},
"headers": ["string_view"],
},
{
"name": "__cpp_lib_constexpr_tuple",
"values": {"c++20": 201811},
"headers": ["tuple"],
},
{
"name": "__cpp_lib_constexpr_typeinfo",
"values": {"c++23": 202106},
"headers": ["typeinfo"],
},
{
"name": "__cpp_lib_constexpr_utility",
"values": {"c++20": 201811},
"headers": ["utility"],
},
{
"name": "__cpp_lib_constexpr_vector",
"values": {"c++20": 201907},
"headers": ["vector"],
},
{
"name": "__cpp_lib_constrained_equality",
"values": {"c++26": 202403},
"headers": ["optional", "tuple", "utility", "variant"],
"unimplemented": True,
},
{
"name": "__cpp_lib_containers_ranges",
"values": {"c++23": 202202},
"headers": [
"deque",
"forward_list",
"list",
"map",
"queue",
"set",
"stack",
"string",
"unordered_map",
"unordered_set",
"vector",
],
},
{
"name": "__cpp_lib_copyable_function",
"values": {"c++26": 202306},
"headers": ["functional"],
"unimplemented": True,
},
{
"name": "__cpp_lib_coroutine",
"values": {"c++20": 201902},
"headers": ["coroutine"],
},
{
"name": "__cpp_lib_debugging",
"values": {
"c++26": 202311,
},
"headers": ["debugging"],
"unimplemented": True,
},
{
"name": "__cpp_lib_default_template_type_for_algorithm_values",
"values": {"c++26": 202403},
"headers": ["algorithm", "deque", "forward_list", "list", "ranges", "string", "vector"],
"unimplemented": True,
},
{
"name": "__cpp_lib_destroying_delete",
"values": {"c++20": 201806},
"headers": ["new"],
"test_suite_guard": "TEST_STD_VER > 17 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",
"libcxx_guard": "_LIBCPP_STD_VER >= 20 && defined(__cpp_impl_destroying_delete) && __cpp_impl_destroying_delete >= 201806L",
},
{
"name": "__cpp_lib_enable_shared_from_this",
"values": {"c++17": 201603},
"headers": ["memory"],
},
{
"name": "__cpp_lib_endian",
"values": {"c++20": 201907},
"headers": ["bit"],
},
{
"name": "__cpp_lib_erase_if",
"values": {"c++20": 202002},
"headers": [
"deque",
"forward_list",
"list",
"map",
"set",
"string",
"unordered_map",
"unordered_set",
"vector",
],
},
{
"name": "__cpp_lib_exchange_function",
"values": {"c++14": 201304},
"headers": ["utility"],
},
{
"name": "__cpp_lib_execution",
"values": {"c++17": 201603, "c++20": 201902},
"headers": ["execution"],
"unimplemented": True,
},
{
"name": "__cpp_lib_expected",
"values": {"c++23": 202211},
"headers": ["expected"],
},
{
"name": "__cpp_lib_filesystem",
"values": {"c++17": 201703},
"headers": ["filesystem"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || (!defined(_LIBCPP_HAS_NO_FILESYSTEM) && _LIBCPP_AVAILABILITY_HAS_FILESYSTEM_LIBRARY)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_FILESYSTEM) && _LIBCPP_AVAILABILITY_HAS_FILESYSTEM_LIBRARY",
},
{
"name": "__cpp_lib_format",
"values": {
"c++20": 202110,
},
"headers": ["format"],
},
{
"name": "__cpp_lib_format_path",
"values": {"c++26": 202403},
"headers": ["filesystem"],
"unimplemented": True,
},
{
"name": "__cpp_lib_format_ranges",
"values": {"c++23": 202207},
"headers": ["format"],
},
{
"name": "__cpp_lib_format_uchar",
"values": {
"c++20": 202311
},
"headers": [
"format"
],
},
{
"name": "__cpp_lib_formatters",
"values": {"c++23": 202302},
"headers": ["stacktrace", "thread"],
"unimplemented": True,
},
{
"name": "__cpp_lib_forward_like",
"values": {"c++23": 202207},
"headers": ["utility"],
},
{
"name": "__cpp_lib_freestanding_algorithm",
"values": {
"c++26": 202311
},
"headers": ["algorithm"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_array",
"values": {
"c++26": 202311
},
"headers": ["array"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_cstring",
"values": {
"c++26": 202306
},
"headers": ["cstring"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_expected",
"values": {
"c++26": 202311
},
"headers": ["expected"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_mdspan",
"values": {
"c++26": 202311
},
"headers": ["mdspan"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_optional",
"values": {
"c++26": 202311
},
"headers": ["optional"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_string_view",
"values": {
"c++26": 202311
},
"headers": ["string_view"],
"unimplemented": True,
},
{
"name": "__cpp_lib_freestanding_variant",
"values": {
"c++26": 202311
},
"headers": ["variant"],
"unimplemented": True,
},
{
"name": "__cpp_lib_fstream_native_handle",
"values": {"c++26": 202306},
"headers": ["fstream"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || (!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION))",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)",
},
{
"name": "__cpp_lib_function_ref",
"values": {
"c++26": 202306
},
"headers": ["functional"],
"unimplemented": True,
},
{
"name": "__cpp_lib_gcd_lcm",
"values": {"c++17": 201606},
"headers": ["numeric"],
},
{
"name": "__cpp_lib_generate_random",
"values": {"c++26": 202403},
"headers": ["random"],
"unimplemented": True,
},
{
"name": "__cpp_lib_generic_associative_lookup",
"values": {"c++14": 201304},
"headers": ["map", "set"],
},
{
"name": "__cpp_lib_generic_unordered_lookup",
"values": {"c++20": 201811},
"headers": ["unordered_map", "unordered_set"],
},
{
"name": "__cpp_lib_hardware_interference_size",
"values": {"c++17": 201703},
"test_suite_guard": "!defined(_LIBCPP_VERSION) || (defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE))",
"libcxx_guard": "defined(__GCC_DESTRUCTIVE_SIZE) && defined(__GCC_CONSTRUCTIVE_SIZE)",
"headers": ["new"],
},
{
"name": "__cpp_lib_has_unique_object_representations",
"values": {"c++17": 201606},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_hazard_pointer",
"values": {"c++26": 202306},
"headers": [
"hazard_pointer"
],
"unimplemented": True,
},
{
"name": "__cpp_lib_hypot",
"values": {"c++17": 201603},
"headers": ["cmath"],
},
{
"name": "__cpp_lib_incomplete_container_elements",
"values": {"c++17": 201505},
"headers": ["forward_list", "list", "vector"],
},
{
"name": "__cpp_lib_inplace_vector",
"values": {"c++26": 202406},
"headers": ["inplace_vector"],
"unimplemented": True,
},
{
"name": "__cpp_lib_int_pow2",
"values": {"c++20": 202002},
"headers": ["bit"],
},
{
"name": "__cpp_lib_integer_comparison_functions",
"values": {"c++20": 202002},
"headers": ["utility"],
},
{
"name": "__cpp_lib_integer_sequence",
"values": {"c++14": 201304},
"headers": ["utility"],
},
{
"name": "__cpp_lib_integral_constant_callable",
"values": {"c++14": 201304},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_interpolate",
"values": {"c++20": 201902},
"headers": ["cmath", "numeric"],
},
{
"name": "__cpp_lib_invoke",
"values": {"c++17": 201411},
"headers": ["functional"],
},
{
"name": "__cpp_lib_invoke_r",
"values": {"c++23": 202106},
"headers": ["functional"],
},
{
"name": "__cpp_lib_ios_noreplace",
"values": {"c++23": 202207},
"headers": ["ios"],
},
{
"name": "__cpp_lib_is_aggregate",
"values": {"c++17": 201703},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_constant_evaluated",
"values": {"c++20": 201811},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_final",
"values": {"c++14": 201402},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_invocable",
"values": {"c++17": 201703},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_layout_compatible",
"values": {"c++20": 201907},
"headers": ["type_traits"],
"unimplemented": True,
},
{
"name": "__cpp_lib_is_nothrow_convertible",
"values": {"c++20": 201806},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_null_pointer",
"values": {"c++14": 201309},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_pointer_interconvertible",
"values": {"c++20": 201907},
"headers": ["type_traits"],
"unimplemented": True,
},
{
"name": "__cpp_lib_is_scoped_enum",
"values": {"c++23": 202011},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_swappable",
"values": {"c++17": 201603},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_is_virtual_base_of",
"values": {
"c++26": 202406
},
"headers": ["type_traits"],
"unimplemented": True,
},
{
"name": "__cpp_lib_is_within_lifetime",
"values": {
"c++26": 202306
},
"headers": ["type_traits"],
"unimplemented": True,
},
{
"name": "__cpp_lib_jthread",
"values": {"c++20": 201911},
"headers": ["stop_token", "thread"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && _LIBCPP_AVAILABILITY_HAS_SYNC",
},
{
"name": "__cpp_lib_latch",
"values": {"c++20": 201907},
"headers": ["latch"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
},
{
"name": "__cpp_lib_launder",
"values": {"c++17": 201606},
"headers": ["new"],
},
{
"name": "__cpp_lib_linalg",
"values": {
"c++26": 202311
},
"headers": ["linalg"],
"unimplemented": True,
},
{
"name": "__cpp_lib_list_remove_return_type",
"values": {"c++20": 201806},
"headers": ["forward_list", "list"],
},
{
"name": "__cpp_lib_logical_traits",
"values": {"c++17": 201510},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_make_from_tuple",
"values": {"c++17": 201606},
"headers": ["tuple"],
},
{
"name": "__cpp_lib_make_reverse_iterator",
"values": {"c++14": 201402},
"headers": ["iterator"],
},
{
"name": "__cpp_lib_make_unique",
"values": {"c++14": 201304},
"headers": ["memory"],
},
{
"name": "__cpp_lib_map_try_emplace",
"values": {"c++17": 201411},
"headers": ["map"],
},
{
"name": "__cpp_lib_math_constants",
"values": {"c++20": 201907},
"headers": ["numbers"],
},
{
"name": "__cpp_lib_math_special_functions",
"values": {"c++17": 201603},
"headers": ["cmath"],
"unimplemented": True,
},
{
"name": "__cpp_lib_mdspan",
"values": {
"c++23": 202207,
"c++26": 202406,
},
"headers": ["mdspan"],
},
{
"name": "__cpp_lib_memory_resource",
"values": {"c++17": 201603},
"headers": ["memory_resource"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",
"libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",
},
{
"name": "__cpp_lib_modules",
"values": {"c++23": 202207},
"headers": [],
},
{
"name": "__cpp_lib_move_iterator_concept",
"values": {"c++20": 202207},
"headers": ["iterator"],
},
{
"name": "__cpp_lib_move_only_function",
"values": {"c++23": 202110},
"headers": ["functional"],
"unimplemented": True,
},
{
"name": "__cpp_lib_node_extract",
"values": {"c++17": 201606},
"headers": ["map", "set", "unordered_map", "unordered_set"],
},
{
"name": "__cpp_lib_nonmember_container_access",
"values": {"c++17": 201411},
"headers": [
"array",
"deque",
"forward_list",
"iterator",
"list",
"map",
"regex",
"set",
"string",
"unordered_map",
"unordered_set",
"vector",
],
},
{
"name": "__cpp_lib_not_fn",
"values": {
"c++17": 201603,
},
"headers": ["functional"],
},
{
"name": "__cpp_lib_null_iterators",
"values": {"c++14": 201304},
"headers": ["iterator"],
},
{
"name": "__cpp_lib_optional",
"values": {"c++17": 201606, "c++23": 202110},
"headers": ["optional"],
},
{
"name": "__cpp_lib_optional_range_support",
"values": {"c++26": 202406},
"headers": ["optional"],
"unimplemented": True,
},
{
"name": "__cpp_lib_out_ptr",
"values": {
"c++23": 202106,
"c++26": 202311,
},
"headers": ["memory"],
},
{
"name": "__cpp_lib_parallel_algorithm",
"values": {"c++17": 201603},
"headers": ["algorithm", "numeric"],
"unimplemented": True,
},
{
"name": "__cpp_lib_philox_engine",
"values": {
"c++26": 202406
},
"headers": ["random"],
"unimplemented": True,
},
{
"name": "__cpp_lib_polymorphic_allocator",
"values": {"c++20": 201902},
"headers": ["memory_resource"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR",
"libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR",
},
{
"name": "__cpp_lib_print",
"values": {
"c++23": 202207,
},
"headers": ["ostream", "print"],
},
{
"name": "__cpp_lib_quoted_string_io",
"values": {"c++14": 201304},
"headers": ["iomanip"],
"test_suite_guard": "!defined(_LIBCPP_VERSION) || !defined(_LIBCPP_HAS_NO_LOCALIZATION)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_LOCALIZATION)",
},
{
"name": "__cpp_lib_ranges",
"values": {
"c++20": 202110,
"c++23": 202211,
},
"headers": ["algorithm", "functional", "iterator", "memory", "ranges"],
},
{
"name": "__cpp_lib_ranges_as_const",
"values": {
"c++23": 202207
},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_as_rvalue",
"values": {"c++23": 202207},
"headers": ["ranges"],
},
{
"name": "__cpp_lib_ranges_chunk",
"values": {"c++23": 202202},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_chunk_by",
"values": {"c++23": 202202},
"headers": ["ranges"],
},
{
"name": "__cpp_lib_ranges_concat",
"values": {"c++26": 202403},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_contains",
"values": {"c++23": 202207},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_ranges_find_last",
"values": {"c++23": 202207},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_ranges_iota",
"values": {"c++23": 202202},
"headers": ["numeric"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_join_with",
"values": {"c++23": 202202},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_repeat",
"values": {"c++23": 202207},
"headers": ["ranges"],
},
{
"name": "__cpp_lib_ranges_slide",
"values": {"c++23": 202202},
"headers": ["ranges"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_starts_ends_with",
"values": {"c++23": 202106},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_ranges_to_container",
"values": {"c++23": 202202},
"headers": ["ranges"],
},
{
"name": "__cpp_lib_ranges_zip",
"values": {"c++23": 202110},
"headers": ["ranges", "tuple", "utility"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ratio",
"values": {"c++26": 202306},
"headers": ["ratio"],
},
{
"name": "__cpp_lib_raw_memory_algorithms",
"values": {"c++17": 201606},
"headers": ["memory"],
},
{
"name": "__cpp_lib_rcu",
"values": {"c++26": 202306},
"headers": [
"rcu"
],
"unimplemented": True,
},
{
"name": "__cpp_lib_reference_from_temporary",
"values": {"c++23": 202202},
"headers": ["type_traits"],
"unimplemented": True,
},
{
"name": "__cpp_lib_reference_wrapper",
"values": {"c++26": 202403},
"headers": ["functional"],
},
{
"name": "__cpp_lib_remove_cvref",
"values": {"c++20": 201711},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_result_of_sfinae",
"values": {"c++14": 201210},
"headers": ["functional", "type_traits"],
},
{
"name": "__cpp_lib_robust_nonmodifying_seq_ops",
"values": {"c++14": 201304},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_sample",
"values": {"c++17": 201603},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_saturation_arithmetic",
"values": {"c++26": 202311},
"headers": ["numeric"],
},
{
"name": "__cpp_lib_scoped_lock",
"values": {"c++17": 201703},
"headers": ["mutex"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
},
{
"name": "__cpp_lib_semaphore",
"values": {"c++20": 201907},
"headers": ["semaphore"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && (!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_SYNC)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && _LIBCPP_AVAILABILITY_HAS_SYNC",
},
{
"name": "__cpp_lib_senders",
"values": {"c++26": 202406},
"headers": ["execution"],
"unimplemented": True,
},
{
"name": "__cpp_lib_shared_mutex",
"values": {"c++17": 201505},
"headers": ["shared_mutex"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
},
{
"name": "__cpp_lib_shared_ptr_arrays",
"values": {"c++17": 201611, "c++20": 201707},
"headers": ["memory"],
},
{
"name": "__cpp_lib_shared_ptr_weak_type",
"values": {"c++17": 201606},
"headers": ["memory"],
},
{
"name": "__cpp_lib_shared_timed_mutex",
"values": {"c++14": 201402},
"headers": ["shared_mutex"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
},
{
"name": "__cpp_lib_shift",
"values": {"c++20": 201806},
"headers": ["algorithm"],
},
{
"name": "__cpp_lib_smart_ptr_for_overwrite",
"values": {"c++20": 202002},
"headers": ["memory"],
"unimplemented": True,
},
{
"name": "__cpp_lib_smart_ptr_owner_equality",
"values": {
"c++26": 202306
},
"headers": ["memory"],
"unimplemented": True,
},
{
"name": "__cpp_lib_source_location",
"values": {"c++20": 201907},
"headers": ["source_location"],
},
{
"name": "__cpp_lib_span",
"values": {
"c++20": 202002,
},
"headers": ["span"],
},
{
"name": "__cpp_lib_span_at",
"values": {"c++26": 202311},
"headers": ["span"],
},
{
"name": "__cpp_lib_span_initializer_list",
"values": {"c++26": 202311},
"headers": ["span"],
},
{
"name": "__cpp_lib_spanstream",
"values": {"c++23": 202106},
"headers": ["spanstream"],
"unimplemented": True,
},
{
"name": "__cpp_lib_ssize",
"values": {"c++20": 201902},
"headers": ["iterator"],
},
{
"name": "__cpp_lib_sstream_from_string_view",
"values": {
"c++26": 202306
},
"headers": ["sstream"],
},
{
"name": "__cpp_lib_stacktrace",
"values": {"c++23": 202011},
"headers": ["stacktrace"],
"unimplemented": True,
},
{
"name": "__cpp_lib_starts_ends_with",
"values": {"c++20": 201711},
"headers": ["string", "string_view"],
},
{
"name": "__cpp_lib_stdatomic_h",
"values": {"c++23": 202011},
"headers": ["stdatomic.h"],
},
{
"name": "__cpp_lib_string_contains",
"values": {"c++23": 202011},
"headers": ["string", "string_view"],
},
{
"name": "__cpp_lib_string_resize_and_overwrite",
"values": {"c++23": 202110},
"headers": ["string"],
},
{
"name": "__cpp_lib_string_udls",
"values": {"c++14": 201304},
"headers": ["string"],
},
{
"name": "__cpp_lib_string_view",
"values": {
"c++17": 201606,
"c++20": 201803,
"c++26": 202403,
},
"headers": ["string", "string_view"],
},
{
"name": "__cpp_lib_submdspan",
"values": {
"c++26": 202306,
},
"headers": ["mdspan"],
"unimplemented": True,
},
{
"name": "__cpp_lib_syncbuf",
"values": {"c++20": 201803},
"headers": ["syncstream"],
"test_suite_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)",
"libcxx_guard": "!defined(_LIBCPP_HAS_NO_EXPERIMENTAL_SYNCSTREAM)",
},
{
"name": "__cpp_lib_text_encoding",
"values": {
"c++26": 202306
},
"headers": ["text_encoding"],
"unimplemented": True,
},
{
"name": "__cpp_lib_three_way_comparison",
"values": {"c++20": 201907},
"headers": ["compare"],
},
{
"name": "__cpp_lib_to_address",
"values": {"c++20": 201711},
"headers": ["memory"],
},
{
"name": "__cpp_lib_to_array",
"values": {"c++20": 201907},
"headers": ["array"],
},
{
"name": "__cpp_lib_to_chars",
"values": {
"c++17": 201611,
"c++26": 202306,
},
"headers": ["charconv"],
"unimplemented": True,
},
{
"name": "__cpp_lib_to_string",
"values": {"c++26": 202306},
"headers": ["string"],
"unimplemented": True,
},
{
"name": "__cpp_lib_to_underlying",
"values": {"c++23": 202102},
"headers": ["utility"],
},
{
"name": "__cpp_lib_transformation_trait_aliases",
"values": {"c++14": 201304},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_transparent_operators",
"values": {"c++14": 201210, "c++17": 201510},
"headers": ["functional", "memory"],
},
{
"name": "__cpp_lib_tuple_element_t",
"values": {"c++14": 201402},
"headers": ["tuple"],
},
{
"name": "__cpp_lib_tuple_like",
"values": {
"c++23": 202207,
"c++26": 202311,
},
"headers": ["map", "tuple", "unordered_map", "utility"],
"unimplemented": True,
},
{
"name": "__cpp_lib_tuples_by_type",
"values": {"c++14": 201304},
"headers": ["tuple", "utility"],
},
{
"name": "__cpp_lib_type_identity",
"values": {"c++20": 201806},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_type_trait_variable_templates",
"values": {"c++17": 201510},
"headers": ["type_traits"],
},
{
"name": "__cpp_lib_uncaught_exceptions",
"values": {"c++17": 201411},
"headers": ["exception"],
},
{
"name": "__cpp_lib_unordered_map_try_emplace",
"values": {"c++17": 201411},
"headers": ["unordered_map"],
},
{
"name": "__cpp_lib_unreachable",
"values": {"c++23": 202202},
"headers": ["utility"],
},
{
"name": "__cpp_lib_unwrap_ref",
"values": {"c++20": 201811},
"headers": ["functional"],
},
{
"name": "__cpp_lib_variant",
"values": {
"c++17": 202102,
},
"headers": ["variant"],
},
{
"name": "__cpp_lib_void_t",
"values": {"c++17": 201411},
"headers": ["type_traits"],
},
]
]
assert feature_test_macros == sorted(feature_test_macros, key=lambda tc: tc["name"])
for tc in feature_test_macros:
assert tc["headers"] == sorted(tc["headers"]), tc
assert ("libcxx_guard" in tc) == ("test_suite_guard" in tc), tc
valid_keys = ["name", "values", "headers", "libcxx_guard", "test_suite_guard", "unimplemented"]
assert all(key in valid_keys for key in tc.keys()), tc
lit_markup = {
"barrier": ["UNSUPPORTED: no-threads"],
"filesystem": ["UNSUPPORTED: no-filesystem"],
"fstream": ["UNSUPPORTED: no-localization"],
"iomanip": ["UNSUPPORTED: no-localization"],
"ios": ["UNSUPPORTED: no-localization"],
"iostream": ["UNSUPPORTED: no-localization"],
"istream": ["UNSUPPORTED: no-localization"],
"latch": ["UNSUPPORTED: no-threads"],
"locale": ["UNSUPPORTED: no-localization"],
"mutex": ["UNSUPPORTED: no-threads"],
"ostream": ["UNSUPPORTED: no-localization"],
"print": ["UNSUPPORTED: no-filesystem"],
"regex": ["UNSUPPORTED: no-localization"],
"semaphore": ["UNSUPPORTED: no-threads"],
"shared_mutex": ["UNSUPPORTED: no-threads"],
"sstream": ["UNSUPPORTED: no-localization"],
"syncstream": ["UNSUPPORTED: no-localization"],
"stdatomic.h": ["UNSUPPORTED: no-threads"],
"stop_token": ["UNSUPPORTED: no-threads"],
"thread": ["UNSUPPORTED: no-threads"],
}
def get_std_dialects():
std_dialects = ["c++14", "c++17", "c++20", "c++23", "c++26"]
return list(std_dialects)
def get_first_std(d):
for s in get_std_dialects():
if s in d.keys():
return s
return None
def get_last_std(d):
rev_dialects = get_std_dialects()
rev_dialects.reverse()
for s in rev_dialects:
if s in d.keys():
return s
return None
def get_std_before(d, std):
std_dialects = get_std_dialects()
candidates = std_dialects[0 : std_dialects.index(std)]
candidates.reverse()
for cand in candidates:
if cand in d.keys():
return cand
return None
def get_value_before(d, std):
new_std = get_std_before(d, std)
if new_std is None:
return None
return d[new_std]
def get_for_std(d, std):
std_dialects = get_std_dialects()
if std not in std_dialects:
return None
std_list = list(std_dialects[0 : std_dialects.index(std) + 1])
std_list.reverse()
for s in std_list:
if s in d.keys():
return d[s]
return None
def get_std_number(std):
return std.replace("c++", "")
"""
Functions to produce the <version> header
"""
def produce_macros_definition_for_std(std):
result = ""
indent = 55
for tc in feature_test_macros:
if std not in tc["values"]:
continue
inner_indent = 1
if "test_suite_guard" in tc.keys():
result += "# if %s\n" % tc["libcxx_guard"]
inner_indent += 2
if get_value_before(tc["values"], std) is not None:
assert "test_suite_guard" not in tc.keys()
result += "# undef %s\n" % tc["name"]
line = "#%sdefine %s" % ((" " * inner_indent), tc["name"])
line += " " * (indent - len(line))
line += " %sL" % tc["values"][std]
if "unimplemented" in tc.keys():
line = "// " + line
result += line
result += "\n"
if "test_suite_guard" in tc.keys():
result += "# endif\n"
return result.strip()
def produce_macros_definitions():
macro_definition_template = """#if _LIBCPP_STD_VER >= {std_number}
{macro_definition}
#endif"""
macros_definitions = []
for std in get_std_dialects():
macros_definitions.append(
macro_definition_template.format(
std_number=get_std_number(std),
macro_definition=produce_macros_definition_for_std(std),
)
)
return "\n\n".join(macros_definitions)
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i : i + n]
def produce_version_synopsis():
indent = 56
header_indent = 56 + len("20XXYYL ")
result = ""
def indent_to(s, val):
if len(s) >= val:
return s
s += " " * (val - len(s))
return s
line = indent_to("Macro name", indent) + "Value"
line = indent_to(line, header_indent) + "Headers"
result += line + "\n"
for tc in feature_test_macros:
prev_defined_std = get_last_std(tc["values"])
line = "{name: <{indent}}{value}L ".format(
name=tc["name"], indent=indent, value=tc["values"][prev_defined_std]
)
headers = list(tc["headers"])
headers.remove("version")
for chunk in chunks(headers, 3):
line = indent_to(line, header_indent)
chunk = ["<%s>" % header for header in chunk]
line += " ".join(chunk)
result += line
result += "\n"
line = ""
while True:
prev_defined_std = get_std_before(tc["values"], prev_defined_std)
if prev_defined_std is None:
break
result += "%s%sL // %s\n" % (
indent_to("", indent),
tc["values"][prev_defined_std],
prev_defined_std.replace("c++", "C++"),
)
return result
def produce_version_header():
template = """// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP_VERSIONH
#define _LIBCPP_VERSIONH
/*
version synopsis
{synopsis}
*/
#include <__config>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
// clang-format off
{cxx_macros}
// clang-format on
#endif // _LIBCPP_VERSIONH
"""
version_str = template.format(
synopsis=produce_version_synopsis().strip(),
cxx_macros=produce_macros_definitions(),
)
version_header_path = os.path.join(include_path, "version")
with open(version_header_path, "w", newline="\n") as f:
f.write(version_str)
"""
Functions to produce test files
"""
test_types = {
"undefined": """
# ifdef {name}
# error "{name} should not be defined before {std_first}"
# endif
""",
"test_suite_guard": """
# if {test_suite_guard}
# ifndef {name}
# error "{name} should be defined in {std}"
# endif
# if {name} != {value}
# error "{name} should have the value {value} in {std}"
# endif
# else
# ifdef {name}
# error "{name} should not be defined when the requirement '{test_suite_guard}' is not met!"
# endif
# endif
""",
"unimplemented": """
# if !defined(_LIBCPP_VERSION)
# ifndef {name}
# error "{name} should be defined in {std}"
# endif
# if {name} != {value}
# error "{name} should have the value {value} in {std}"
# endif
# else // _LIBCPP_VERSION
# ifdef {name}
# error "{name} should not be defined because it is unimplemented in libc++!"
# endif
# endif
""",
"defined": """
# ifndef {name}
# error "{name} should be defined in {std}"
# endif
# if {name} != {value}
# error "{name} should have the value {value} in {std}"
# endif
""",
}
def generate_std_test(test_list, std):
result = ""
for tc in test_list:
val = get_for_std(tc["values"], std)
if val is not None:
val = "%sL" % val
if val is None:
result += test_types["undefined"].format(
name=tc["name"], std_first=get_first_std(tc["values"])
)
elif "unimplemented" in tc.keys():
result += test_types["unimplemented"].format(
name=tc["name"], value=val, std=std
)
elif "test_suite_guard" in tc.keys():
result += test_types["test_suite_guard"].format(
name=tc["name"],
value=val,
std=std,
test_suite_guard=tc["test_suite_guard"],
)
else:
result += test_types["defined"].format(name=tc["name"], value=val, std=std)
return result.strip()
def generate_std_tests(test_list):
std_tests_template = """#if TEST_STD_VER < {first_std_number}
{pre_std_test}
{other_std_tests}
#elif TEST_STD_VER > {penultimate_std_number}
{last_std_test}
#endif // TEST_STD_VER > {penultimate_std_number}"""
std_dialects = get_std_dialects()
other_std_tests = []
for std in std_dialects[:-1]:
other_std_tests.append("#elif TEST_STD_VER == " + get_std_number(std))
other_std_tests.append(generate_std_test(test_list, std))
std_tests = std_tests_template.format(
first_std_number=get_std_number(std_dialects[0]),
pre_std_test=generate_std_test(test_list, "c++11"),
other_std_tests="\n\n".join(other_std_tests),
penultimate_std_number=get_std_number(std_dialects[-2]),
last_std_test=generate_std_test(test_list, std_dialects[-1]),
)
return std_tests
def generate_synopsis(test_list):
max_name_len = max([len(tc["name"]) for tc in test_list])
indent = max_name_len + 8
def mk_line(prefix, suffix):
return "{prefix: <{max_len}}{suffix}\n".format(
prefix=prefix, suffix=suffix, max_len=indent
)
result = ""
result += mk_line("/* Constant", "Value")
for tc in test_list:
prefix = " %s" % tc["name"]
for std in [s for s in get_std_dialects() if s in tc["values"].keys()]:
result += mk_line(
prefix, "%sL [%s]" % (tc["values"][std], std.replace("c++", "C++"))
)
prefix = ""
result += "*/"
return result
def produce_tests():
headers = set([h for tc in feature_test_macros for h in tc["headers"]])
for h in headers:
test_list = [tc for tc in feature_test_macros if h in tc["headers"]]
if not has_header(h):
for tc in test_list:
assert "unimplemented" in tc.keys()
continue
markup = "\n".join("// " + tag for tag in lit_markup.get(h, []))
test_body = """//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// WARNING: This test was generated by {script_name}
// and should not be edited manually.
//
// clang-format off
{markup}
// <{header}>
// Test the feature test macros defined by <{header}>
{synopsis}
#include <{header}>
#include "test_macros.h"
{cxx_tests}
""".format(
script_name=script_name,
header=h,
markup=("\n{}\n".format(markup) if markup else ""),
synopsis=generate_synopsis(test_list),
cxx_tests=generate_std_tests(test_list),
)
test_name = "{header}.version.compile.pass.cpp".format(header=h)
out_path = os.path.join(macro_test_path, test_name)
with open(out_path, "w", newline="\n") as f:
f.write(test_body)
"""
Produce documentation for the feature test macros
"""
def make_widths(grid):
widths = []
for i in range(0, len(grid[0])):
cell_width = 2 + max(
reduce(lambda x, y: x + y, [[len(row[i])] for row in grid], [])
)
widths += [cell_width]
return widths
def create_table(grid, indent):
indent_str = " " * indent
col_widths = make_widths(grid)
result = [indent_str + add_divider(col_widths, 2)]
header_flag = 2
for row_i in range(0, len(grid)):
row = grid[row_i]
line = indent_str + " ".join(
[pad_cell(row[i], col_widths[i]) for i in range(0, len(row))]
)
result.append(line.rstrip())
if row_i == len(grid) - 1:
header_flag = 2
if row[0].startswith("**"):
header_flag += 1
separator = indent_str + add_divider(col_widths, header_flag)
result.append(separator.rstrip())
header_flag = 0
return "\n".join(result)
def add_divider(widths, header_flag):
if header_flag == 3:
return "=".join(["=" * w for w in widths])
if header_flag == 2:
return " ".join(["=" * w for w in widths])
if header_flag == 1:
return "-".join(["-" * w for w in widths])
else:
return " ".join(["-" * w for w in widths])
def pad_cell(s, length, left_align=True):
padding = (length - len(s)) * " "
return s + padding
def get_status_table():
table = [["Macro Name", "Value"]]
for std in get_std_dialects():
table += [["**" + std.replace("c++", "C++") + "**", ""]]
for tc in feature_test_macros:
if std not in tc["values"].keys():
continue
value = "``%sL``" % tc["values"][std]
if "unimplemented" in tc.keys():
value = "*unimplemented*"
table += [["``%s``" % tc["name"], value]]
return table
def produce_docs():
doc_str = """.. _FeatureTestMacroTable:
==========================
Feature Test Macro Support
==========================
.. contents::
:local:
Overview
========
This file documents the feature test macros currently supported by libc++.
.. _feature-status:
Status
======
.. table:: Current Status
:name: feature-status-table
:widths: auto
{status_tables}
""".format(
status_tables=create_table(get_status_table(), 4)
)
table_doc_path = os.path.join(docs_path, "FeatureTestMacroTable.rst")
with open(table_doc_path, "w", newline="\n") as f:
f.write(doc_str)
def get_ftms(
data, std_dialects: List[str], use_implemented_status: bool
) -> Dict[str, Dict[str, Any]]:
"""Impementation for FeatureTestMacros.(standard|implemented)_ftms()."""
result = dict()
for feature in data:
last = None
entry = dict()
implemented = True
for std in std_dialects:
if std not in feature["values"].keys():
if last == None:
continue
else:
entry[std] = last
else:
if implemented:
values = feature["values"][std]
assert len(values) > 0, f"{feature['name']}[{std}] has no entries"
for value in values:
papers = list(values[value])
assert (
len(papers) > 0
), f"{feature['name']}[{std}][{value}] has no entries"
for paper in papers:
if use_implemented_status and not paper["implemented"]:
implemented = False
break
if implemented:
last = f"{value}L"
else:
break
entry[std] = last
result[feature["name"]] = entry
return result
class FeatureTestMacros:
"""Provides all feature-test macro (FTM) output components.
The class has several generators to use the feature-test macros in libc++:
- FTM status page
- The version header and its tests
This class is not intended to duplicate
https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations#library-feature-test-macros
SD-FeatureTest: Feature-Test Macros and Policies
Historically libc++ did not list all papers affecting a FTM, the new data
structure is able to do that. However there is no intention to add the
historical data. After papers have been implemented this information can be
removed. For example, __cpp_lib_format's value 201907 requires 3 papers,
once implemented it can be reduced to 1 paper and remove the paper number
and title. This would reduce the size of the data.
The input data is stored in the following JSON format:
[ # A list with multiple feature-test macro entries.
{
# required
# The name of the feature test macro. These names should be unique and
# sorted in the list.
"name": "__cpp_lib_any",
# required
# A map with the value of the FTM based on the language standard. Only
# the versions in which the value of the FTM changes are listed. For
# example, this macro's value does not change in C++20 so it does not
# list C++20. If it changes in C++26, it will have entries for C++17 and
# C++26.
"values": {
# required
# The language standard, also named dialect in this class.
"c++17": {
# required
# The value of the feature test macro. This contains an array with
# one or more papers that need to be implemented before this value
# is considered implemented.
"201606": [
{
# optional
# Contains the paper number that is part of the FTM version.
"number": "P0220R1",
# optional
# Contains the title of the paper that is part of the FTM
# version.
"title": "Adopt Library Fundamentals V1 TS Components for C++17"
# required
# The implementation status of the paper.
"implemented": true
}
]
}
},
# required
# A sorted list of headers that should provide the FTM. The header
# <version> is automatically added to this list. This list could be
# empty. For example, __cpp_lib_modules is only present in version.
# Requiring the field makes it easier to detect accidental omission.
"headers": [
"any"
],
# optional, required when libcxx_guard is present
# This field is used only to generate the unit tests for the
# feature-test macros. It can't depend on macros defined in <__config>
# because the `test/std/` parts of the test suite are intended to be
# portable to any C++ standard library implementation, not just libc++.
# It may depend on
# * macros defined by the compiler itself, or
# * macros generated by CMake.
# In some cases we add also depend on macros defined in
# <__availability>.
"test_suite_guard": "!defined(_LIBCPP_VERSION) || _LIBCPP_AVAILABILITY_HAS_PMR"
# optional, required when test_suite_guard is present
# This field is used only to guard the feature-test macro in
# <version>. It may be the same as `test_suite_guard`, or it may
# depend on macros defined in <__config>.
"libcxx_guard": "_LIBCPP_AVAILABILITY_HAS_PMR"
},
]
"""
__data = None
def __init__(self, filename: str):
"""Initializes the class with the JSON data in the file 'filename'."""
self.__data = json.load(open(filename))
@functools.cached_property
def std_dialects(self) -> List[str]:
"""Returns the C++ dialects avaiable.
The available dialects are based on the 'c++xy' keys found the 'values'
entries in '__data'. So when WG21 starts to feature-test macros for a
future C++ Standard this dialect will automatically be available.
The return value is a sorted list with the C++ dialects used. Since FTM
were added in C++14 the list will not contain C++03 or C++11.
"""
dialects = set()
for feature in self.__data:
keys = feature["values"].keys()
assert len(keys) > 0, "'values' is empty"
dialects |= keys
return sorted(list(dialects))
@functools.cached_property
def standard_ftms(self) -> Dict[str, Dict[str, Any]]:
"""Returns the FTM versions per dialect in the Standard.
This function does not use the 'implemented' flag. The output contains
the versions used in the Standard. When a FTM in libc++ is not
implemented according to the Standard to output may opt to show the
expected value.
The result is a dict with the following content
- key: Name of the feature test macro.
- value: A dict with the following content:
* key: The version of the C++ dialect.
* value: The value of the feature-test macro.
"""
return get_ftms(self.__data, self.std_dialects, False)
@functools.cached_property
def implemented_ftms(self) -> Dict[str, Dict[str, Any]]:
"""Returns the FTM versions per dialect implemented in libc++.
Unlike `get_std_dialect_versions` this function uses the 'implemented'
flag. This returns the actual implementation status in libc++.
The result is a dict with the following content
- key: Name of the feature test macro.
- value: A dict with the following content:
* key: The version of the C++ dialect.
* value: The value of the feature-test macro. When a feature-test
macro is not implemented its value is None.
"""
return get_ftms(self.__data, self.std_dialects, True)
def main():
produce_version_header()
produce_tests()
produce_docs()
if __name__ == "__main__":
main()