Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -99,4 +99,4 @@
"typescript": "^5.0.0",
"vitest": "^1.0.0"
}
}
}
28 changes: 20 additions & 8 deletions src/runtime/include/iec_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,21 +892,33 @@ inline bool NE_STRING(const IECString<MaxLen1>& s1, const IECString<MaxLen2>& 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<typename T,
std::enable_if_t<std::is_integral_v<decltype(iec_unwrap(std::declval<T>()))>, int> = 0>
std::enable_if_t<std::is_integral<decltype(iec_unwrap(std::declval<T>()))>::value
&& std::is_unsigned<decltype(iec_unwrap(std::declval<T>()))>::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<decltype(raw)>) {
std::snprintf(buf, sizeof(buf), "%llu", static_cast<unsigned long long>(raw));
} else {
std::snprintf(buf, sizeof(buf), "%lld", static_cast<long long>(raw));
}
std::snprintf(buf, sizeof(buf), "%llu", static_cast<unsigned long long>(iec_unwrap(v)));
return IECString<254>(buf);
}

template<typename T,
std::enable_if_t<std::is_integral<decltype(iec_unwrap(std::declval<T>()))>::value
&& !std::is_unsigned<decltype(iec_unwrap(std::declval<T>()))>::value, int> = 0>
inline IECString<254> TO_STRING(T v) noexcept {
char buf[32];
std::snprintf(buf, sizeof(buf), "%lld", static_cast<long long>(iec_unwrap(v)));
return IECString<254>(buf);
}

template<typename T,
std::enable_if_t<std::is_floating_point_v<decltype(iec_unwrap(std::declval<T>()))>, int> = 0>
std::enable_if_t<std::is_floating_point<decltype(iec_unwrap(std::declval<T>()))>::value, int> = 0>
inline IECString<254> TO_STRING(T v) noexcept {
char buf[64];
std::snprintf(buf, sizeof(buf), "%g", static_cast<double>(iec_unwrap(v)));
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/include/iec_subrange.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ namespace strucpp {
* @tparam Lower The minimum allowed value (inclusive)
* @tparam Upper The maximum allowed value (inclusive)
*/
template<typename BaseType, auto Lower, auto Upper>
template<typename BaseType, BaseType Lower, BaseType Upper>
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_;
Expand Down Expand Up @@ -208,13 +208,13 @@ class IEC_SUBRANGE_Value {
* @tparam Lower The minimum allowed value
* @tparam Upper The maximum allowed value
*/
template<typename BaseType, auto Lower, auto Upper>
template<typename BaseType, BaseType Lower, BaseType Upper>
class IEC_SUBRANGE_Var {
public:
using value_type = IEC_SUBRANGE_Value<BaseType, Lower, Upper>;
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_;
Expand Down Expand Up @@ -323,7 +323,7 @@ class IEC_SUBRANGE_Var {
* Convenience alias for subrange with forcing support.
* Usage: IEC_SUBRANGE<int16_t, 0, 100> percentage;
*/
template<typename BaseType, auto Lower, auto Upper>
template<typename BaseType, BaseType Lower, BaseType Upper>
using IEC_SUBRANGE = IEC_SUBRANGE_Var<BaseType, Lower, Upper>;

/*
Expand Down
61 changes: 33 additions & 28 deletions src/runtime/include/iec_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ template<typename T, typename Bounds1, typename Bounds2, typename Bounds3> class
class IEC_STRUCT_Base;
template<typename EnumType> class IEC_ENUM_Value;
template<typename EnumType> class IEC_ENUM_Var;
template<typename BaseType, auto Lower, auto Upper> class IEC_SUBRANGE_Value;
template<typename BaseType, auto Lower, auto Upper> 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<typename BaseType, BaseType Lower, BaseType Upper> class IEC_SUBRANGE_Value;
template<typename BaseType, BaseType Lower, BaseType Upper> class IEC_SUBRANGE_Var;
template<typename T> class IEC_REF_TO;

// =============================================================================
Expand Down Expand Up @@ -232,43 +237,43 @@ struct is_any_elementary<IECVar<T>> : is_any_elementary<T> {};
// =============================================================================

template<typename T>
inline constexpr bool is_iec_type_v = is_iec_type<T>::value;
constexpr bool is_iec_type_v = is_iec_type<T>::value;

template<typename T>
inline constexpr bool is_any_bool_v = is_any_bool<T>::value;
constexpr bool is_any_bool_v = is_any_bool<T>::value;

template<typename T>
inline constexpr bool is_any_sint_v = is_any_sint<T>::value;
constexpr bool is_any_sint_v = is_any_sint<T>::value;

template<typename T>
inline constexpr bool is_any_uint_v = is_any_uint<T>::value;
constexpr bool is_any_uint_v = is_any_uint<T>::value;

template<typename T>
inline constexpr bool is_any_int_v = is_any_int<T>::value;
constexpr bool is_any_int_v = is_any_int<T>::value;

template<typename T>
inline constexpr bool is_any_real_v = is_any_real<T>::value;
constexpr bool is_any_real_v = is_any_real<T>::value;

template<typename T>
inline constexpr bool is_any_num_v = is_any_num<T>::value;
constexpr bool is_any_num_v = is_any_num<T>::value;

template<typename T>
inline constexpr bool is_any_bit_v = is_any_bit<T>::value;
constexpr bool is_any_bit_v = is_any_bit<T>::value;

template<typename T>
inline constexpr bool is_any_string_v = is_any_string<T>::value;
constexpr bool is_any_string_v = is_any_string<T>::value;

template<typename T>
inline constexpr bool is_any_date_v = is_any_date<T>::value;
constexpr bool is_any_date_v = is_any_date<T>::value;

template<typename T>
inline constexpr bool is_any_time_v = is_any_time<T>::value;
constexpr bool is_any_time_v = is_any_time<T>::value;

template<typename T>
inline constexpr bool is_any_magnitude_v = is_any_magnitude<T>::value;
constexpr bool is_any_magnitude_v = is_any_magnitude<T>::value;

template<typename T>
inline constexpr bool is_any_elementary_v = is_any_elementary<T>::value;
constexpr bool is_any_elementary_v = is_any_elementary<T>::value;

// =============================================================================
// Type Size Traits
Expand Down Expand Up @@ -297,7 +302,7 @@ template<typename T>
struct iec_bit_size<IECVar<T>> : iec_bit_size<T> {};

template<typename T>
inline constexpr size_t iec_bit_size_v = iec_bit_size<T>::value;
constexpr size_t iec_bit_size_v = iec_bit_size<T>::value;

// =============================================================================
// Underlying Type Traits
Expand All @@ -315,10 +320,10 @@ using iec_underlying_type_t = typename iec_underlying_type<T>::type;

/** Extract the raw value from an IECVar or pass through a raw value unchanged */
template<typename T>
inline constexpr T iec_unwrap(T v) noexcept { return v; }
constexpr T iec_unwrap(T v) noexcept { return v; }

template<typename T>
inline constexpr T iec_unwrap(const IECVar<T>& v) noexcept { return v.get(); }
constexpr T iec_unwrap(const IECVar<T>& v) noexcept { return v.get(); }

// =============================================================================
// Type Limits
Expand Down Expand Up @@ -389,16 +394,16 @@ template<typename T, typename Bnd1, typename Bnd2, typename Bnd3>
struct is_iec_array<IEC_ARRAY_3D<T, Bnd1, Bnd2, Bnd3>> : std::true_type {};

template<typename T>
inline constexpr bool is_iec_array_v = is_iec_array<T>::value;
constexpr bool is_iec_array_v = is_iec_array<T>::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<typename T>
struct is_iec_struct : std::bool_constant<std::is_base_of_v<IEC_STRUCT_Base, T>> {};
struct is_iec_struct : std::integral_constant<bool,std::is_base_of<IEC_STRUCT_Base, T>::value> {};

template<typename T>
inline constexpr bool is_iec_struct_v = is_iec_struct<T>::value;
constexpr bool is_iec_struct_v = is_iec_struct<T>::value;

/** Check if T is an IEC enumeration type */
template<typename T> struct is_iec_enum : std::false_type {};
Expand All @@ -410,19 +415,19 @@ template<typename E>
struct is_iec_enum<IEC_ENUM_Var<E>> : std::true_type {};

template<typename T>
inline constexpr bool is_iec_enum_v = is_iec_enum<T>::value;
constexpr bool is_iec_enum_v = is_iec_enum<T>::value;

/** Check if T is an IEC subrange type */
template<typename T> struct is_iec_subrange : std::false_type {};

template<typename B, auto L, auto U>
template<typename B, B L, B U>
struct is_iec_subrange<IEC_SUBRANGE_Value<B, L, U>> : std::true_type {};

template<typename B, auto L, auto U>
template<typename B, B L, B U>
struct is_iec_subrange<IEC_SUBRANGE_Var<B, L, U>> : std::true_type {};

template<typename T>
inline constexpr bool is_iec_subrange_v = is_iec_subrange<T>::value;
constexpr bool is_iec_subrange_v = is_iec_subrange<T>::value;

/** Check if T is an IEC pointer type (REF_TO) */
template<typename T> struct is_iec_pointer : std::false_type {};
Expand All @@ -431,11 +436,11 @@ template<typename T>
struct is_iec_pointer<IEC_REF_TO<T>> : std::true_type {};

template<typename T>
inline constexpr bool is_iec_pointer_v = is_iec_pointer<T>::value;
constexpr bool is_iec_pointer_v = is_iec_pointer<T>::value;

/** Check if T is ANY_DERIVED (composite types: arrays, structs, enums, subranges, pointers) */
template<typename T>
struct is_any_derived : std::bool_constant<
struct is_any_derived : std::integral_constant<bool,
is_iec_array<T>::value ||
is_iec_struct<T>::value ||
is_iec_enum<T>::value ||
Expand All @@ -444,7 +449,7 @@ struct is_any_derived : std::bool_constant<
> {};

template<typename T>
inline constexpr bool is_any_derived_v = is_any_derived<T>::value;
constexpr bool is_any_derived_v = is_any_derived<T>::value;

// =============================================================================
// C++17 SFINAE Helpers
Expand Down
16 changes: 8 additions & 8 deletions src/runtime/include/iec_var.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class IECVar {
* to functions expecting a wider IECVar type. Without this, C++ would need
* two user-defined conversions (IECVar<U>→U→T→IECVar<T>) which is disallowed. */
template<typename U, std::enable_if_t<
std::is_convertible_v<U, T> && !std::is_same_v<U, T>, int> = 0>
std::is_convertible<U, T>::value && !std::is_same<U, T>::value, int> = 0>
IECVar(const IECVar<U>& other) noexcept
: value_{static_cast<T>(other.get())}, forced_{false}, forced_value_{} {}

Expand Down Expand Up @@ -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<typename U, std::enable_if_t<
std::is_convertible_v<U, T> && !std::is_same_v<U, T>, int> = 0>
std::is_convertible<U, T>::value && !std::is_same<U, T>::value, int> = 0>
IECVar& operator=(const IECVar<U>& other) noexcept {
set(static_cast<T>(other.get()));
return *this;
Expand All @@ -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<typename U, typename V = T, std::enable_if_t<std::is_integral_v<V>, int> = 0>
template<typename U, typename V = T, std::enable_if_t<std::is_integral<V>::value, int> = 0>
IECVar& operator=(const IEC_Ptr<U>& ptr) noexcept {
set(static_cast<T>(ptr.to_addr()));
return *this;
Expand All @@ -218,10 +218,10 @@ class IECVar {
// =========================================================================

/** Forward operator-> to underlying type (struct/FB member access) */
template<typename U = T, std::enable_if_t<std::is_class_v<U>, int> = 0>
template<typename U = T, std::enable_if_t<std::is_class<U>::value, int> = 0>
T* operator->() noexcept { return &value_; }

template<typename U = T, std::enable_if_t<std::is_class_v<U>, int> = 0>
template<typename U = T, std::enable_if_t<std::is_class<U>::value, int> = 0>
const T* operator->() const noexcept { return &value_; }

/** Forward operator[] to underlying type (1D array access) */
Expand Down Expand Up @@ -350,7 +350,7 @@ inline IECVar<T> operator/(const IECVar<T>& a, const IECVar<T>& b) noexcept {
return IECVar<T>(a.get() / b.get());
}

template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
inline IECVar<T> operator%(const IECVar<T>& a, const IECVar<T>& b) noexcept {
return IECVar<T>(a.get() % b.get());
}
Expand All @@ -364,8 +364,8 @@ template<typename T> inline IECVar<T> operator*(const IECVar<T>& a, T b) noexcep
template<typename T> inline IECVar<T> operator*(T a, const IECVar<T>& b) noexcept { return IECVar<T>(a * b.get()); }
template<typename T> inline IECVar<T> operator/(const IECVar<T>& a, T b) noexcept { return IECVar<T>(a.get() / b); }
template<typename T> inline IECVar<T> operator/(T a, const IECVar<T>& b) noexcept { return IECVar<T>(a / b.get()); }
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>> inline IECVar<T> operator%(const IECVar<T>& a, T b) noexcept { return IECVar<T>(a.get() % b); }
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>> inline IECVar<T> operator%(T a, const IECVar<T>& b) noexcept { return IECVar<T>(a % b.get()); }
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> inline IECVar<T> operator%(const IECVar<T>& a, T b) noexcept { return IECVar<T>(a.get() % b); }
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> inline IECVar<T> operator%(T a, const IECVar<T>& b) noexcept { return IECVar<T>(a % b.get()); }

// =============================================================================
// Comparison Operators
Expand Down
Loading