New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rewrite sf::Event
API to improve type safety
#2766
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2766 +/- ##
==========================================
+ Coverage 43.65% 44.61% +0.95%
==========================================
Files 253 255 +2
Lines 20940 21132 +192
Branches 5139 5212 +73
==========================================
+ Hits 9142 9427 +285
+ Misses 10785 10711 -74
+ Partials 1013 994 -19
... and 58 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
sf::Event
API to avoid UBsf::Event
API to improve type safety
2d3122d
to
c3e903e
Compare
c3e903e
to
292c5ad
Compare
I contained the templates so you can't just pass any random template argument into it. |
85c8cab
to
93e3fcf
Compare
I removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for writing this PoC! 💪
Looking at the event handling,
switch (event.type)
{
case sf::Event::Type::KeyPressed:
if (event.get<sf::Event::KeyPressed>().code == sf::Keyboard::Escape)
window.close();
break;
case ...
}
I wonder if we could consider some alternative idioms to reduce repetition a bit?
Example:
if (auto* key = event.as<sf::Event::KeyPressed>()) {
if (key.code == sf::Keyboard::Escape)
window.close();
}
// no break needed
else ...
or even:
if (auto* key = event.keyPressed(); key && key.code == sf::Keyboard::Escape)
window.close();
}
// no break needed
else ...
This would also avoid the age-old type safety problem: you cannot accidentally check for type X and process Y. The concrete type is directly returned.
I'm fine adding an analog to |
I added I'm still questioning whether I want to keep the non- |
54ffa39
to
ea50b15
Compare
https://github.com/ChrisThrasher/mandelbrot/compare/event_api Quick experiment trying the new API in a project of mine. I love how I can still use a |
ea50b15
to
dc0aa81
Compare
I pushed a commit that improves error messages. Previously you'd get a failed |
SFML/imgui-sfml@master...event_api Here is what it looks like to apply this API to ImGui-SFML. Writing this uncovered a potential shortcoming of this API case sf::Event::Type::KeyPressed: // fall-through
case sf::Event::Type::KeyReleased: {
const bool down = (event.type == sf::Event::KeyPressed);
const ImGuiKey mod = keycodeToImGuiMod(event.key.code);
// The modifier booleans are not reliable when it's the modifier
// itself that's being pressed. Detect these presses directly.
if (mod != ImGuiKey_None) {
io.AddKeyEvent(mod, down);
} else {
io.AddKeyEvent(ImGuiKey_ModCtrl, event.key.control);
io.AddKeyEvent(ImGuiKey_ModShift, event.key.shift);
io.AddKeyEvent(ImGuiKey_ModAlt, event.key.alt);
io.AddKeyEvent(ImGuiKey_ModSuper, event.key.system);
}
const ImGuiKey key = keycodeToImGuiKey(event.key.code);
io.AddKeyEvent(key, down);
io.SetKeyEventNativeData(key, event.key.code, -1);
} break; In the event where fall through behavior is desired, this new API requires duplicating the code inside each The only solution I can think of (other than having users simply deal with this) is to put both press and release events into one event with a new field for disambiguating between whether the key was pressed or released. I'm not sure I like this though. |
Pointed one of my personal projects towards the For someone used to SFML I think this kind of API change will be mostly unnoticed but still yielding better type safety. For new users I think this poses no more diffficulty to grasp than the old form, but with the benefit of better type safety. Think this is a great PoC for showing how SFML could modernise events, hope to see it get further along and potentially become the new way in SFML3. |
I took the remaining events that include |
Periodic rebase. No changes made. |
API DesignFrom an API design point of view I have three comments:
Switch ErgonomicsPersonally, I've often fallen back to My ProposalSeeing most of the voice speaking up for support of I'd then be very interested in seeing potential proposals to "simplify" the API. |
547634d
to
ec29718
Compare
I removed |
6edf32b
to
3ae2d35
Compare
3ae2d35
to
dda70dd
Compare
Incorporated Kimci's review comments and rebased on |
dda70dd
to
0f20b6a
Compare
Rebased on |
struct MouseEntered | ||
{ | ||
Sensor::Type type; //!< Type of the sensor | ||
float x; //!< Current value of the sensor on X axis | ||
float y; //!< Current value of the sensor on Y axis | ||
float z; //!< Current value of the sensor on Z axis | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as a random thought that just occurred to me: Since we're handling mouse position anyway to track inside/outside, I'd consider MouseEntered
and MouseLeft
to be MouseMoved
events that should report a position, if supported.
3fb00d0
to
1377d57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has been open for a long time and discussed even longer before that 🙂
I think it's in a good state to be merged; if there are more things to refine, they could be done as follow-up PRs.
Thanks for the initiative, as well as all the continuous updates and rebases @ChrisThrasher! 👍
This new API is built on top of std::variant. This allows us to store many different event types in a space-efficient way and access the active event type in a type-safe manner that eliminates the categories of UB that are possible with unions. Co-authored-by: kimci86 <kimci86@hotmail.fr>
1377d57
to
0423c45
Compare
Rebased on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think, this will be the final form of the API for SFML 3, but let us collect some more feedback from people using it, by getting it into master.
Thank you for all the work put into @ChrisThrasher and all the discuss around it so far from everyone! I believe it's a good thing that we the "old" API is being challenged and forces us to reflect some more about the design and safety.
Description
Closes #2668
This new API is built on top of std::variant. This allows us to store many different event types in a space-efficient way and access the active event type in a type-safe manner that eliminates the types of UB that are possible with unions.
I tried to find docs that referenced the old API but may have missed a few places. Initially my focus is on consensus for API design. After that we can focus harder on updating all the docs. There are also some missing comment blocks I need to fill in and more tests to write so there is some finishing work to do.
The current implementation has fewer structs than values in the enumeration. This means there is not a 1-to-1 mapping between event types and data structures. By ensuring there is a 1-to-1 mapping it lets us use the variant index to get the enumeration value. This is very easy to write, read, and maintain. Believe me this is a lot better than alternative implementations that don't add new structs.
I'm reluctant to add this because adding an element to a variant constitutes an API break for anyone visiting that variant. You must visit a variant with the perfect set of callbacks for the elements in the variant. If we allow users to visit this variant then we're effectively preventing ourselves from adding new events in a given major release. For now I'd prefer to retain that ability. We can always add this later if desired.
EDIT 28 Jan: Apparently it is possible to support
std::visit
without prohibiting us from adding new events so I'm open to this idea.I highly value letting users continue to use
switch
to process potential event states. I think that's a very useful, powerful way to list all the actions one may take in response to a given event type. It's also nice to know that users don't have to remove theswitch
statements they've been writing for years. While the syntax for event processing are different, a lot of their existing knowledge can be carried forward. Users can still writeif
/else if
chains if they prefer but they're not forced to.I don't see this as a problem. Any exceptions only occur in incorrect programs so no one writing correct code will ever deal with them. The exceptions will server to very quickly catch bugs in user programs. To avoid leaking this detail would require we jump through extra hoops and possibly introduce new avenues for UB that I would rather not do.
EDIT 16 Apr 2024: The rest of the team disagrees about the value of returning an enumeration so I removed the APIs that used the enum. I still stand by this as a good API but don't want to stall this PR indefinitely so I removed it.