Skip to content

fix(runtime): make user-facing headers compile under gnu++14 (mbed cores)#168

Merged
thiagoralves merged 1 commit into
developmentfrom
fix/runtime-headers-cxx14-bridge
Jun 5, 2026
Merged

fix(runtime): make user-facing headers compile under gnu++14 (mbed cores)#168
thiagoralves merged 1 commit into
developmentfrom
fix/runtime-headers-cxx14-bridge

Conversation

@thiagoralves
Copy link
Copy Markdown
Contributor

Summary

Backports incidental C++17 features out of the four strucpp runtime headers reachable from c_blocks_code.cpp's transitive include chain (iec_var.hpp, iec_string.hpp, iec_traits.hpp, iec_subrange.hpp).

User code in openplc-editor's Arduino flow compiles c_blocks_code.cpp under whatever `-std=` the platform's Arduino core picked. Every mbed-based Arduino core — `mbed_nano` (Nano RP2040 Connect, Nano 33 BLE), `mbed_opta` (Opta), `mbed_giga` (GIGA), `mbed_portenta`, `mbed_edge` — hard-codes `-std=gnu++14` in its `cxxflags.txt`. On those targets every C/C++ POU compile was failing with cascading `'is_convertible_v' is not a member of 'std'` / `'auto' parameter not permitted in this context` errors out of these headers.

What changed

Purely syntactic; no semantic change:

  • `iec_var.hpp`: 8× `std::is_X_v` → `std::is_X::value`
  • `iec_string.hpp`: 3× `std::is_X_v` → `std::is_X::value`; `TO_STRING(integral)` rewritten from `if constexpr` (C++17) into two SFINAE'd overloads (signed/unsigned), same behaviour
  • `iec_traits.hpp`: 3× `_v` substitutions; 2× `std::bool_constant` → `std::integral_constant<bool, X>`; 4× `template<typename B, auto L, auto U>` → typed `template<typename B, B L, B U>`; 14× `inline constexpr` variable templates → `constexpr` (inline variables are C++17; variable templates without `inline` are C++14)
  • `iec_subrange.hpp`: primary `IEC_SUBRANGE_Value` / `IEC_SUBRANGE_Var` template heads + `IEC_SUBRANGE` alias switched from `auto Lower, auto Upper` to `BaseType Lower, BaseType Upper` to match the forward decls in `iec_traits.hpp` and stay consistent

The precompile stage in openplc-editor / openplc-web continues to use `-std=gnu++17` and the strucpp runtime CMake test suite still requires C++17 — nothing in this PR downgrades the strucpp project's own toolchain.

Test plan

  • Compile a c_blocks_code.cpp-equivalent TU under `-std=gnu++14`: passes (was failing on every mbed-cored Arduino board)
  • Compile the same TU under `-std=gnu++17`: passes (the previously-working AVR / rp2040 path)
  • Both compiles produce byte-identical 2552-byte `.o` files → same ABI under either standard
  • End-to-end test on openplc-editor with patched headers: Nano RP2040 Connect project with a C/C++ Function Block compiles cleanly (user-verified)

User code that includes the strucpp runtime through a generated
`c_blocks_code.cpp` (openplc-editor's Arduino flow) compiles under
whatever `-std=` the platform's Arduino core picked.  Most cores use
gnu++17 or later, but **every mbed-based Arduino core** — `mbed_nano`
(Nano RP2040 Connect, Nano 33 BLE), `mbed_opta` (Opta), `mbed_giga`
(GIGA), `mbed_portenta`, `mbed_edge` — hard-codes `-std=gnu++14` in
its `cxxflags.txt`.  On those targets every C/C++ POU compile fails
with a cascade of `'is_convertible_v' is not a member of 'std'` etc.
out of `iec_var.hpp` / `iec_string.hpp` / `iec_traits.hpp`.

The strucpp runtime tests + the precompile stage of the openplc
build keep using C++17, so this commit doesn't downgrade anything
there — it just removes incidental C++17 usage from the four headers
reachable from `c_blocks_code.cpp`'s transitive include chain.

Substitutions (purely syntactic, no semantic change):

  iec_var.hpp:
    - `std::is_X_v<T>` (×8) → `std::is_X<T>::value`

  iec_string.hpp:
    - `std::is_X_v<T>` (×3) → `std::is_X<T>::value`
    - `TO_STRING(integral)` rewritten from `if constexpr` (C++17) into
      two SFINAE'd overloads (signed vs unsigned).  Same behaviour;
      tag-dispatch is C++11-clean.

  iec_traits.hpp:
    - `std::is_X_v<T>` (×3) → `std::is_X<T>::value`
    - `std::bool_constant<X>` (×2) → `std::integral_constant<bool, X>`
    - `template<typename B, auto L, auto U>` (×4) → typed bounds
      `template<typename B, B L, B U>` (subrange specs + forward decls)
    - 14 `inline constexpr ...` variable templates → `constexpr ...`
      (inline variables are C++17; variable templates without `inline`
      are C++14 and work for header-only metaprogramming where the
      address is never taken)

  iec_subrange.hpp:
    - Primary `IEC_SUBRANGE_Value` / `IEC_SUBRANGE_Var` template heads
      and the matching `IEC_SUBRANGE` alias switched from `auto Lower,
      auto Upper` to `BaseType Lower, BaseType Upper`.  IEC subrange
      bounds are always the base type by the standard, so this is no
      capability loss and keeps the declaration consistent with the
      forward decls in `iec_traits.hpp`.

ABI verification: compiled the headers under both `-std=gnu++14` and
`-std=gnu++17` against a c_blocks_code.cpp-equivalent translation
unit; both produce byte-identical 2552-byte `.o` files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant