diff --git a/package-lock.json b/package-lock.json index a117be7d..47fb48b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "strucpp", - "version": "0.5.4", + "version": "0.5.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "strucpp", - "version": "0.5.4", + "version": "0.5.5", "license": "GPL-3.0-or-later", "dependencies": { "chevrotain": "^11.0.0" diff --git a/package.json b/package.json index a46f8658..b1b83db8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "strucpp", - "version": "0.5.4", + "version": "0.5.5", "description": "IEC 61131-3 Structured Text to C++ Compiler", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -99,4 +99,4 @@ "typescript": "^5.0.0", "vitest": "^1.0.0" } -} \ No newline at end of file +} diff --git a/src/runtime/include/iec_string.hpp b/src/runtime/include/iec_string.hpp index 060c9993..780af4d1 100644 --- a/src/runtime/include/iec_string.hpp +++ b/src/runtime/include/iec_string.hpp @@ -892,21 +892,33 @@ inline bool NE_STRING(const IECString& s1, const IECString& s2 // ============================================================================= // Numeric → STRING (uses snprintf for real-time safety, no std::to_string) +// +// Split into two SFINAE'd overloads (signed vs unsigned) rather than a single +// function with `if constexpr`, because user C/C++ POU code in +// `c_blocks_code.cpp` compiles under whatever -std= the platform's Arduino +// core picked (gnu++14 on mbed cores). `if constexpr` is C++17-only and +// would make this header unusable from those compilation units. Tag +// dispatch via `enable_if_t` works back to C++11. template()))>, int> = 0> + std::enable_if_t()))>::value + && std::is_unsigned()))>::value, int> = 0> inline IECString<254> TO_STRING(T v) noexcept { char buf[32]; - auto raw = iec_unwrap(v); - if constexpr (std::is_unsigned_v) { - std::snprintf(buf, sizeof(buf), "%llu", static_cast(raw)); - } else { - std::snprintf(buf, sizeof(buf), "%lld", static_cast(raw)); - } + std::snprintf(buf, sizeof(buf), "%llu", static_cast(iec_unwrap(v))); + return IECString<254>(buf); +} + +template()))>::value + && !std::is_unsigned()))>::value, int> = 0> +inline IECString<254> TO_STRING(T v) noexcept { + char buf[32]; + std::snprintf(buf, sizeof(buf), "%lld", static_cast(iec_unwrap(v))); return IECString<254>(buf); } template()))>, int> = 0> + std::enable_if_t()))>::value, int> = 0> inline IECString<254> TO_STRING(T v) noexcept { char buf[64]; std::snprintf(buf, sizeof(buf), "%g", static_cast(iec_unwrap(v))); diff --git a/src/runtime/include/iec_subrange.hpp b/src/runtime/include/iec_subrange.hpp index c784cd05..b7feb2e0 100644 --- a/src/runtime/include/iec_subrange.hpp +++ b/src/runtime/include/iec_subrange.hpp @@ -25,12 +25,12 @@ namespace strucpp { * @tparam Lower The minimum allowed value (inclusive) * @tparam Upper The maximum allowed value (inclusive) */ -template +template class IEC_SUBRANGE_Value { public: using base_type = BaseType; - static constexpr auto lower_bound = Lower; - static constexpr auto upper_bound = Upper; + static constexpr BaseType lower_bound = Lower; + static constexpr BaseType upper_bound = Upper; private: BaseType value_; @@ -208,13 +208,13 @@ class IEC_SUBRANGE_Value { * @tparam Lower The minimum allowed value * @tparam Upper The maximum allowed value */ -template +template class IEC_SUBRANGE_Var { public: using value_type = IEC_SUBRANGE_Value; using base_type = BaseType; - static constexpr auto lower_bound = Lower; - static constexpr auto upper_bound = Upper; + static constexpr BaseType lower_bound = Lower; + static constexpr BaseType upper_bound = Upper; private: value_type value_; @@ -323,7 +323,7 @@ class IEC_SUBRANGE_Var { * Convenience alias for subrange with forcing support. * Usage: IEC_SUBRANGE percentage; */ -template +template using IEC_SUBRANGE = IEC_SUBRANGE_Var; /* diff --git a/src/runtime/include/iec_traits.hpp b/src/runtime/include/iec_traits.hpp index 9eae9a73..4ba83225 100644 --- a/src/runtime/include/iec_traits.hpp +++ b/src/runtime/include/iec_traits.hpp @@ -30,8 +30,13 @@ template class class IEC_STRUCT_Base; template class IEC_ENUM_Value; template class IEC_ENUM_Var; -template class IEC_SUBRANGE_Value; -template class IEC_SUBRANGE_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; // ============================================================================= @@ -232,43 +237,43 @@ struct is_any_elementary> : is_any_elementary {}; // ============================================================================= template -inline constexpr bool is_iec_type_v = is_iec_type::value; +constexpr bool is_iec_type_v = is_iec_type::value; template -inline constexpr bool is_any_bool_v = is_any_bool::value; +constexpr bool is_any_bool_v = is_any_bool::value; template -inline constexpr bool is_any_sint_v = is_any_sint::value; +constexpr bool is_any_sint_v = is_any_sint::value; template -inline constexpr bool is_any_uint_v = is_any_uint::value; +constexpr bool is_any_uint_v = is_any_uint::value; template -inline constexpr bool is_any_int_v = is_any_int::value; +constexpr bool is_any_int_v = is_any_int::value; template -inline constexpr bool is_any_real_v = is_any_real::value; +constexpr bool is_any_real_v = is_any_real::value; template -inline constexpr bool is_any_num_v = is_any_num::value; +constexpr bool is_any_num_v = is_any_num::value; template -inline constexpr bool is_any_bit_v = is_any_bit::value; +constexpr bool is_any_bit_v = is_any_bit::value; template -inline constexpr bool is_any_string_v = is_any_string::value; +constexpr bool is_any_string_v = is_any_string::value; template -inline constexpr bool is_any_date_v = is_any_date::value; +constexpr bool is_any_date_v = is_any_date::value; template -inline constexpr bool is_any_time_v = is_any_time::value; +constexpr bool is_any_time_v = is_any_time::value; template -inline constexpr bool is_any_magnitude_v = is_any_magnitude::value; +constexpr bool is_any_magnitude_v = is_any_magnitude::value; template -inline constexpr bool is_any_elementary_v = is_any_elementary::value; +constexpr bool is_any_elementary_v = is_any_elementary::value; // ============================================================================= // Type Size Traits @@ -297,7 +302,7 @@ template struct iec_bit_size> : iec_bit_size {}; template -inline constexpr size_t iec_bit_size_v = iec_bit_size::value; +constexpr size_t iec_bit_size_v = iec_bit_size::value; // ============================================================================= // Underlying Type Traits @@ -315,10 +320,10 @@ 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 -inline constexpr T iec_unwrap(T v) noexcept { return v; } +constexpr T iec_unwrap(T v) noexcept { return v; } template -inline constexpr T iec_unwrap(const IECVar& v) noexcept { return v.get(); } +constexpr T iec_unwrap(const IECVar& v) noexcept { return v.get(); } // ============================================================================= // Type Limits @@ -389,16 +394,16 @@ template struct is_iec_array> : std::true_type {}; template -inline constexpr bool is_iec_array_v = is_iec_array::value; +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::bool_constant> {}; +struct is_iec_struct : std::integral_constant::value> {}; template -inline constexpr bool is_iec_struct_v = is_iec_struct::value; +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 {}; @@ -410,19 +415,19 @@ template struct is_iec_enum> : std::true_type {}; template -inline constexpr bool is_iec_enum_v = is_iec_enum::value; +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 +template struct is_iec_subrange> : std::true_type {}; -template +template struct is_iec_subrange> : std::true_type {}; template -inline constexpr bool is_iec_subrange_v = is_iec_subrange::value; +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 {}; @@ -431,11 +436,11 @@ template struct is_iec_pointer> : std::true_type {}; template -inline constexpr bool is_iec_pointer_v = is_iec_pointer::value; +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::bool_constant< +struct is_any_derived : std::integral_constant::value || is_iec_struct::value || is_iec_enum::value || @@ -444,7 +449,7 @@ struct is_any_derived : std::bool_constant< > {}; template -inline constexpr bool is_any_derived_v = is_any_derived::value; +constexpr bool is_any_derived_v = is_any_derived::value; // ============================================================================= // C++17 SFINAE Helpers diff --git a/src/runtime/include/iec_var.hpp b/src/runtime/include/iec_var.hpp index 8dbd519a..b7cbef12 100644 --- a/src/runtime/include/iec_var.hpp +++ b/src/runtime/include/iec_var.hpp @@ -59,7 +59,7 @@ class IECVar { * to functions expecting a wider IECVar type. Without this, C++ would need * two user-defined conversions (IECVar→U→T→IECVar) which is disallowed. */ template && !std::is_same_v, int> = 0> + std::is_convertible::value && !std::is_same::value, int> = 0> IECVar(const IECVar& other) noexcept : value_{static_cast(other.get())}, forced_{false}, forced_value_{} {} @@ -197,7 +197,7 @@ class IECVar { * by providing a direct match (template is preferred over two indirect paths * that each require one user-defined conversion). */ template && !std::is_same_v, int> = 0> + std::is_convertible::value && !std::is_same::value, int> = 0> IECVar& operator=(const IECVar& other) noexcept { set(static_cast(other.get())); return *this; @@ -207,7 +207,7 @@ class IECVar { * WARNING: On 64-bit platforms, assigning to types narrower than pointer width * (e.g., DWORD) truncates the address. Use ULINT, LWORD, or PTR_INT_t for * portable pointer-to-integer storage. */ - template, int> = 0> + template::value, int> = 0> IECVar& operator=(const IEC_Ptr& ptr) noexcept { set(static_cast(ptr.to_addr())); return *this; @@ -218,10 +218,10 @@ class IECVar { // ========================================================================= /** Forward operator-> to underlying type (struct/FB member access) */ - template, int> = 0> + template::value, int> = 0> T* operator->() noexcept { return &value_; } - template, int> = 0> + template::value, int> = 0> const T* operator->() const noexcept { return &value_; } /** Forward operator[] to underlying type (1D array access) */ @@ -350,7 +350,7 @@ inline IECVar operator/(const IECVar& a, const IECVar& b) noexcept { return IECVar(a.get() / b.get()); } -template>> +template::value>> inline IECVar operator%(const IECVar& a, const IECVar& b) noexcept { return IECVar(a.get() % b.get()); } @@ -364,8 +364,8 @@ template inline IECVar operator*(const IECVar& a, T b) noexcep template inline IECVar operator*(T a, const IECVar& b) noexcept { return IECVar(a * b.get()); } template inline IECVar operator/(const IECVar& a, T b) noexcept { return IECVar(a.get() / b); } template inline IECVar operator/(T a, const IECVar& b) noexcept { return IECVar(a / b.get()); } -template>> inline IECVar operator%(const IECVar& a, T b) noexcept { return IECVar(a.get() % b); } -template>> inline IECVar operator%(T a, const IECVar& b) noexcept { return IECVar(a % b.get()); } +template::value>> inline IECVar operator%(const IECVar& a, T b) noexcept { return IECVar(a.get() % b); } +template::value>> inline IECVar operator%(T a, const IECVar& b) noexcept { return IECVar(a % b.get()); } // ============================================================================= // Comparison Operators