// SPDX-License-Identifier: GPL-3.0-or-later WITH STruCpp-runtime-exception // Copyright (C) 2025 Autonomy / OpenPLC Project // This file is part of the STruC++ Runtime Library and is covered by the // STruC++ Runtime Library Exception. See COPYING.RUNTIME for details. // // ============================================================================ // WARNING: KEEP THIS HEADER C++14-CLEAN. // ---------------------------------------------------------------------------- // strucpp targets C++17 and its EMITTED code is compiled as C++17 — but this // header is part of the C/C++ Function Block include chain, which is NOT. // OpenPLC Editor's Arduino flow emits `c_blocks_code.cpp`, including // `iec_var.hpp` + `iec_string.hpp` (which transitively pull in `iec_traits.hpp` // and `iec_types.hpp`). That translation unit is compiled under whatever // `-std=` the Arduino core picks, and every mbed-based core — Nano RP2040 // Connect, Nano 33 BLE, Opta, GIGA, Portenta, Edge — hard-codes `-std=gnu++14`. // So any C++17/20 construct reachable from here breaks the user's C/C++ POU // build, even though the rest of strucpp is happily on C++17. // // In this header (and anything it includes) do NOT use C++17/20 features // unguarded. In particular: // * `std::trait_v` -> `std::trait::value` // * `if constexpr` -> SFINAE / tag dispatch // * inline variables / `inline constexpr` // * `auto` non-type template params -> typed NTTPs // * C++17/20 library headers (, , , // , ...) -> include ONLY behind `#if __cplusplus >= ...` // (see the guarded block in iec_types.hpp for the pattern). // // Boundary introduced in commit be85d8a. If you change which headers // `c_blocks_code.cpp` pulls in, update this set of warnings accordingly. // ============================================================================ /** * STruC++ Runtime - IEC Type Traits * * This header provides C++ type traits and concepts for IEC 61131-3 type categories. * These traits enable compile-time type checking and generic programming for * standard library functions. */ #pragma once #include #include #include #include "iec_types.hpp" namespace strucpp { // Forward declarations template class IECVar; // Forward declarations for composite types template struct ArrayBounds; template class IEC_ARRAY_1D; template class IEC_ARRAY_2D; template class IEC_ARRAY_3D; class IEC_STRUCT_Base; template class IEC_ENUM_Value; template class IEC_ENUM_Var; // Forward declarations. `auto` template parameters are C++17; we declare // these subrange templates with `BaseType`-typed bounds so they remain // compilable under the platform's gnu++14 default (mbed Arduino cores). // Numeric bounds in IEC subrange types are always the same type as the // base, so requiring `Lower`/`Upper` to be `BaseType` is no semantic loss. template class IEC_SUBRANGE_Value; template class IEC_SUBRANGE_Var; template class IEC_REF_TO; // ============================================================================= // Primary Type Traits (default to false) // ============================================================================= /** Check if T is an IEC type */ template struct is_iec_type : std::false_type {}; /** Check if T is ANY_BOOL (just BOOL) */ template struct is_any_bool : std::false_type {}; /** Check if T is ANY_SINT (signed integers) */ template struct is_any_sint : std::false_type {}; /** Check if T is ANY_UINT (unsigned integers) */ template struct is_any_uint : std::false_type {}; /** Check if T is ANY_INT (all integers) */ template struct is_any_int : std::false_type {}; /** Check if T is ANY_REAL (floating point) */ template struct is_any_real : std::false_type {}; /** Check if T is ANY_NUM (numeric: integers + reals) */ template struct is_any_num : std::false_type {}; /** Check if T is ANY_BIT (bit strings including BOOL) */ template struct is_any_bit : std::false_type {}; /** Check if T is ANY_STRING (strings and characters) */ template struct is_any_string : std::false_type {}; /** Check if T is ANY_DATE (date/time types) */ template struct is_any_date : std::false_type {}; /** Check if T is ANY_TIME (duration types: TIME, LTIME) */ template struct is_any_time : std::false_type {}; /** Check if T is ANY_MAGNITUDE (numeric + time) */ template struct is_any_magnitude : std::false_type {}; /** Check if T is ANY_ELEMENTARY (all elementary types) */ template struct is_any_elementary : std::false_type {}; // ============================================================================= // Specializations for Raw C++ Types // ============================================================================= // Note: IEC types share underlying C++ types, so we specialize on the actual // C++ types. The categorization reflects the primary IEC use case. // Boolean (BOOL_t = bool) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_bool : std::true_type {}; template<> struct is_any_bit : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 8-bit unsigned (BYTE_t/USINT_t = uint8_t) - categorized as bit string template<> struct is_iec_type : std::true_type {}; template<> struct is_any_bit : std::true_type {}; template<> struct is_any_uint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 8-bit signed (SINT_t = int8_t) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_sint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 16-bit unsigned (WORD_t/UINT_t = uint16_t) - categorized as bit string template<> struct is_iec_type : std::true_type {}; template<> struct is_any_bit : std::true_type {}; template<> struct is_any_uint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 16-bit signed (INT_t = int16_t) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_sint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 32-bit unsigned (DWORD_t/UDINT_t = uint32_t) - categorized as bit string template<> struct is_iec_type : std::true_type {}; template<> struct is_any_bit : std::true_type {}; template<> struct is_any_uint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 32-bit signed (DINT_t = int32_t) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_sint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 64-bit unsigned (LWORD_t/ULINT_t = uint64_t) - categorized as bit string template<> struct is_iec_type : std::true_type {}; template<> struct is_any_bit : std::true_type {}; template<> struct is_any_uint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // 64-bit signed (LINT_t/TIME_t/DATE_t/TOD_t/DT_t/etc = int64_t) // Note: This type is used for both integers and date/time types template<> struct is_iec_type : std::true_type {}; template<> struct is_any_sint : std::true_type {}; template<> struct is_any_int : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_time : std::true_type {}; template<> struct is_any_date : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // Single precision float (REAL_t = float) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_real : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // Double precision float (LREAL_t = double) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_real : std::true_type {}; template<> struct is_any_num : std::true_type {}; template<> struct is_any_magnitude : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // Character (CHAR_t = char) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_string : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // Wide character (WCHAR_t = char16_t) template<> struct is_iec_type : std::true_type {}; template<> struct is_any_string : std::true_type {}; template<> struct is_any_elementary : std::true_type {}; // ============================================================================= // Specializations for IECVar Wrapped Types // ============================================================================= template struct is_iec_type> : is_iec_type {}; template struct is_any_bool> : is_any_bool {}; template struct is_any_sint> : is_any_sint {}; template struct is_any_uint> : is_any_uint {}; template struct is_any_int> : is_any_int {}; template struct is_any_real> : is_any_real {}; template struct is_any_num> : is_any_num {}; template struct is_any_bit> : is_any_bit {}; template struct is_any_string> : is_any_string {}; template struct is_any_date> : is_any_date {}; template struct is_any_time> : is_any_time {}; template struct is_any_magnitude> : is_any_magnitude {}; template struct is_any_elementary> : is_any_elementary {}; // ============================================================================= // Helper Variable Templates (C++17) // ============================================================================= template constexpr bool is_iec_type_v = is_iec_type::value; template constexpr bool is_any_bool_v = is_any_bool::value; template constexpr bool is_any_sint_v = is_any_sint::value; template constexpr bool is_any_uint_v = is_any_uint::value; template constexpr bool is_any_int_v = is_any_int::value; template constexpr bool is_any_real_v = is_any_real::value; template constexpr bool is_any_num_v = is_any_num::value; template constexpr bool is_any_bit_v = is_any_bit::value; template constexpr bool is_any_string_v = is_any_string::value; template constexpr bool is_any_date_v = is_any_date::value; template constexpr bool is_any_time_v = is_any_time::value; template constexpr bool is_any_magnitude_v = is_any_magnitude::value; template constexpr bool is_any_elementary_v = is_any_elementary::value; // ============================================================================= // Type Size Traits // ============================================================================= /** Get the size in bits for an IEC type */ template struct iec_bit_size; // Note: Using actual C++ types to avoid duplicate specializations template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; template<> struct iec_bit_size : std::integral_constant {}; // IECVar wrapper template struct iec_bit_size> : iec_bit_size {}; template constexpr size_t iec_bit_size_v = iec_bit_size::value; // ============================================================================= // Underlying Type Traits // ============================================================================= /** Get the underlying C++ type for an IEC type */ template struct iec_underlying_type { using type = T; }; // IECVar wrapper - extract the underlying type template struct iec_underlying_type> { using type = T; }; template using iec_underlying_type_t = typename iec_underlying_type::type; /** Extract the raw value from an IECVar or pass through a raw value unchanged */ template constexpr T iec_unwrap(T v) noexcept { return v; } template constexpr T iec_unwrap(const IECVar& v) noexcept { return v.get(); } // ============================================================================= // Type Limits // ============================================================================= /** Get the minimum and maximum values for an IEC type */ template struct iec_limits; // Note: Using actual C++ types to avoid duplicate specializations template<> struct iec_limits { static constexpr int8_t min() { return -128; } static constexpr int8_t max() { return 127; } }; template<> struct iec_limits { static constexpr int16_t min() { return -32768; } static constexpr int16_t max() { return 32767; } }; template<> struct iec_limits { static constexpr int32_t min() { return -2147483648; } static constexpr int32_t max() { return 2147483647; } }; template<> struct iec_limits { static constexpr int64_t min() { return INT64_MIN; } static constexpr int64_t max() { return INT64_MAX; } }; template<> struct iec_limits { static constexpr uint8_t min() { return 0; } static constexpr uint8_t max() { return 255; } }; template<> struct iec_limits { static constexpr uint16_t min() { return 0; } static constexpr uint16_t max() { return 65535; } }; template<> struct iec_limits { static constexpr uint32_t min() { return 0; } static constexpr uint32_t max() { return 4294967295U; } }; template<> struct iec_limits { static constexpr uint64_t min() { return 0; } static constexpr uint64_t max() { return UINT64_MAX; } }; // IECVar wrapper template struct iec_limits> : iec_limits {}; // ============================================================================= // Composite Type Traits // ============================================================================= /** Check if T is an IEC array type */ template struct is_iec_array : std::false_type {}; template struct is_iec_array> : std::true_type {}; template struct is_iec_array> : std::true_type {}; template struct is_iec_array> : std::true_type {}; template constexpr bool is_iec_array_v = is_iec_array::value; /** Check if T is an IEC struct type */ // Uses std::is_base_of to detect types derived from IEC_STRUCT_Base // Note: Generated structs inherit from IEC_STRUCT_Base template struct is_iec_struct : std::integral_constant::value> {}; template constexpr bool is_iec_struct_v = is_iec_struct::value; /** Check if T is an IEC enumeration type */ template struct is_iec_enum : std::false_type {}; template struct is_iec_enum> : std::true_type {}; template struct is_iec_enum> : std::true_type {}; template constexpr bool is_iec_enum_v = is_iec_enum::value; /** Check if T is an IEC subrange type */ template struct is_iec_subrange : std::false_type {}; template struct is_iec_subrange> : std::true_type {}; template struct is_iec_subrange> : std::true_type {}; template constexpr bool is_iec_subrange_v = is_iec_subrange::value; /** Check if T is an IEC pointer type (REF_TO) */ template struct is_iec_pointer : std::false_type {}; template struct is_iec_pointer> : std::true_type {}; template constexpr bool is_iec_pointer_v = is_iec_pointer::value; /** Check if T is ANY_DERIVED (composite types: arrays, structs, enums, subranges, pointers) */ template struct is_any_derived : std::integral_constant::value || is_iec_struct::value || is_iec_enum::value || is_iec_subrange::value || is_iec_pointer::value > {}; template constexpr bool is_any_derived_v = is_any_derived::value; // ============================================================================= // C++17 SFINAE Helpers // ============================================================================= template using enable_if_any_int = std::enable_if_t, int>; template using enable_if_any_real = std::enable_if_t, int>; template using enable_if_any_num = std::enable_if_t, int>; template using enable_if_any_bit = std::enable_if_t, int>; template using enable_if_any_string = std::enable_if_t, int>; template using enable_if_any_date = std::enable_if_t, int>; template using enable_if_any_time = std::enable_if_t, int>; template using enable_if_any_magnitude = std::enable_if_t, int>; template using enable_if_any_elementary = std::enable_if_t, int>; template using enable_if_iec_array = std::enable_if_t, int>; template using enable_if_iec_struct = std::enable_if_t, int>; template using enable_if_iec_enum = std::enable_if_t, int>; template using enable_if_iec_subrange = std::enable_if_t, int>; template using enable_if_any_derived = std::enable_if_t, int>; template using enable_if_iec_pointer = std::enable_if_t, int>; // ============================================================================= // C++20 Concepts (when available) // ============================================================================= #if __cplusplus >= 202002L #include /** Concept for IEC types */ template concept IECType = is_iec_type_v; /** Concept for ANY_BOOL types */ template concept AnyBool = is_any_bool_v; /** Concept for ANY_SINT types (signed integers) */ template concept AnySInt = is_any_sint_v; /** Concept for ANY_UINT types (unsigned integers) */ template concept AnyUInt = is_any_uint_v; /** Concept for ANY_INT types (all integers) */ template concept AnyInt = is_any_int_v; /** Concept for ANY_REAL types */ template concept AnyReal = is_any_real_v; /** Concept for ANY_NUM types */ template concept AnyNum = is_any_num_v; /** Concept for ANY_BIT types */ template concept AnyBit = is_any_bit_v; /** Concept for ANY_STRING types */ template concept AnyString = is_any_string_v; /** Concept for ANY_DATE types */ template concept AnyDate = is_any_date_v; /** Concept for ANY_TIME types (durations) */ template concept AnyTime = is_any_time_v; /** Concept for ANY_MAGNITUDE types */ template concept AnyMagnitude = is_any_magnitude_v; /** Concept for ANY_ELEMENTARY types */ template concept AnyElementary = is_any_elementary_v; /** Concept for IEC array types */ template concept IECArray = is_iec_array_v; /** Concept for IEC struct types */ template concept IECStruct = is_iec_struct_v; /** Concept for IEC enum types */ template concept IECEnum = is_iec_enum_v; /** Concept for IEC subrange types */ template concept IECSubrange = is_iec_subrange_v; /** Concept for ANY_DERIVED types (composite types) */ template concept AnyDerived = is_any_derived_v; /** Concept for IEC pointer types (REF_TO) */ template concept IECPointer = is_iec_pointer_v; #endif // C++20 } // namespace strucpp