Skip to content

Replace std::result_of (removed in C++20) with std::invoke_result - #155

Open
jsmbennett wants to merge 2 commits into
Kazade:nextfrom
jsmbennett:fix-result-of-removed-in-cxx20
Open

Replace std::result_of (removed in C++20) with std::invoke_result#155
jsmbennett wants to merge 2 commits into
Kazade:nextfrom
jsmbennett:fix-result-of-removed-in-cxx20

Conversation

@jsmbennett

Copy link
Copy Markdown

Problem

std::result_of was deprecated in C++17 and removed in C++20. libstdc++ still ships it as an extension, so GCC/Linux builds never notice — but libc++ does not, so the engine currently fails to compile on macOS (and any other libc++ target):

error: no template named 'result_of' in namespace 'std'

Because simulant/signals/signal.h is reachable from nearly every public header, this breaks essentially every translation unit — both in-tree and in downstream projects that link the engine. On AppleClang 21 / macOS 15 arm64 a clean build produces 35 such errors.

Changes

simulant/threads/future.h — the local result_of_t alias is reimplemented on std::invoke_result via a small partial specialization, so the existing result_of_t<Function(Args...)> call sites in async() are untouched.

simulant/coroutines/helpers.hstd::result_of<Func()>::typestd::invoke_result<Func>::type (3 sites, including the two friend declarations).

simulant/signals/signal.h — removes typedef std::result_of<Signature> result;.

This last one is deliberately not a mechanical substitution, and is worth a second look. Signature here is a function type such as void(int), so result_of treats the return type void as the callable rather than the signature's return type. Since C++14 result_of is SFINAE-friendly, so std::result_of<void(int)> is simply an empty struct with no ::type:

std::result_of<void(int)> has ::type = 0
sizeof = 1 (empty struct)

It is unused in-tree (grep finds no reference to signal<...>::result) and can't be meaningfully used out of tree, so removing it seemed better than translating a typedef that never worked. Happy to instead define it as a real return-type trait if you'd prefer to keep the name.

Compatibility

std::invoke_result is C++17, so this preserves the C++17 compatibility added in 2175228. The replacement trait was checked against functors, lambdas, function pointers and void-returning callables under both -std=c++17 and -std=c++20.

Testing

  • Engine builds clean on macOS 15 / arm64, AppleClang 21, libc++ (SIMULANT_BUILD_SAMPLES=OFF SIMULANT_BUILD_TESTS=OFF) — previously failed outright.
  • A downstream project links the resulting libsimulant.dylib and runs correctly against the GL2X renderer.
  • No Linux/GCC regression is expected since std::invoke_result is standard in both libstdc++ and libc++; I don't have a Linux box to hand, so a CI run there would be worth confirming.

🤖 Generated with Claude Code

jsmbennett and others added 2 commits August 8, 2026 08:21
std::result_of was deprecated in C++17 and removed in C++20. libstdc++
still provides it as an extension, so GCC/Linux builds are unaffected,
but libc++ does not -- which makes the engine fail to compile on macOS
(and any other libc++ target) with:

    error: no template named 'result_of' in namespace 'std'

Since simulant/signals/signal.h is reachable from nearly every public
header, this breaks essentially every translation unit, including those
of downstream projects linking the engine.

Three changes:

* threads/future.h: reimplement the local result_of_t alias on top of
  std::invoke_result via a small partial specialization, so the existing
  result_of_t<Function(Args...)> call sites are unchanged.

* coroutines/helpers.h: std::result_of<Func()>::type is spelled
  std::invoke_result<Func>::type.

* signals/signal.h: drop `typedef std::result_of<Signature> result;`.
  This one is not a mechanical substitution: Signature is a function
  type such as void(int), so result_of treats the return type void as
  the callable and the trait has no ::type at all. It resolves to an
  empty struct, is unused in-tree, and cannot be meaningfully used out
  of tree, so it is removed rather than translated.

std::invoke_result is C++17, so this keeps the C++17 compatibility added
in 2175228. Verified by building the engine on macOS/arm64 with
AppleClang and libc++, where it previously failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AppConfig::resizable (default false, so existing behaviour is unchanged)
is threaded to the platform window via Window::set_resizable(), which
Application::construct_window calls before create_window -- SDL bakes the
resize flag in at SDL_CreateWindow time. A setter rather than a
create_window parameter keeps the KOSWindow/PSPWindow/AndroidWindow
signatures untouched; those backends simply never read the flag.

SDL2Window then ORs in SDL_WINDOW_RESIZABLE, applies a 320x240 minimum
size, and handles SDL_WINDOWEVENT_SIZE_CHANGED (rather than RESIZED,
which misses SDL_SetWindowSize and fullscreen toggles) by pushing the new
size through the new Window::set_size(). That updates width_/height_ and
dispatches EventListener::on_window_resize, joining the existing
on_window_focus/blur/minimize/restore family.

Consumers only need to recompute camera aspect ratios: Viewport stores
ratios of the render target, so the compositor resolves glViewport from
the new size every frame on its own.

Also fixes a latent bug in the same handler: SDL_PollEvent is global and
initialize_screen() can create a second "Virtual Screen" window, so
SDL_WINDOWEVENT now filters on windowID before touching focus state.

Note that engine code caching window size at construction (stats_panel,
scenes/splash, input_manager's normalized coordinates) will be stale
after a resize; those are untouched here.

Co-Authored-By: Claude Opus 5 (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