[Edit] Now that I think about it, this might as well be its own topic. Feel free to split, if you want to keep this thread focused on the high-level roadmap, without going into detail about C++ related API changes.
Maybe we should start discussing the bigger API changes, especially those that break existing code.
I'm just brainstorming here, so all I write is neither complete nor definitive.
Here's a good overview over new library and language features for all C++ standards.Error HandlingIt would be nice if resources could be returned from factories (named constructors), such as:
sf::Texture tex = sf::Texture::loadFromFile(...);
sf::Image img = sf::Image::loadFromMemory(...);
It would need to be discussed what to do in the error case.
- Exceptions: Nice to use in the happy path, but can be forgotten. Would require exception hierarchy and error discrimination/display mechanism. Requires exception safety everywhere.
- std::optional<T> -- Can be empty for failure, but there's no way to convey the error.
- std::variant<T, E> -- Either the type or an error. Like Rust's Result, but very unidiomatic to use in C++.
- sf::Result<T> or sf::Result<T, E> -- a simplified version of the above, tailored to SFML. Could have syntax sugar like * dereference.
Major removed APIsMain candidate I see is
sf::Thread.
With it come
sf::ThreadLocal and
sf::ThreadLocalPtr.
It can be discussed whether
sf::Time is necessary given
std::chrono, but the latter is very generic and can be overwhelming for the small amount of functionality that SFML needs. Interop (conversion from/to) should definitely be provided though.
Filesystem and pathC++17 Filesystem could benefit both public API and implementation.
If it's too tedious to construct strings to be used in place of
std::filesystem::path, convenience overloads for
sf::String or
const char* can be offered.
Ownership and smart pointersI can't think of a vast number of places where SFML would benefit from smart pointers, but I could imagine that
std::unique_ptr and
std::shared_ptr could clarify ownership semantics, when the library does not simply "borrow" (reference to) a user-created object, but (partially) own it.
std::unique_ptr in particular is a good abstraction boundary, when pointers to abstract base classes, such as
sf::Drawable or
sf::Shape have to be moved (ownership-transferred).
Apart from that, move semantics should be employed in the library, mainly for heavy resources.
New emerged idiomsSmaller things like:
- passing {x, y} for sf::Vector2 parameters, and subsequently a smaller number of constructors.
- If there are callback-like classes, instead of having an abstract base class with 1 method, we could use std::function. SFML doesn't use that a lot though.
- std::byte instead of char/void pointers.
- enum struct
- Lambda expressions mostly to simplify the implementation. As expressions, they have no direct use in the API.
- Range based for loops
- [[deprecated]] attribute
- nullptr everywhere, NULL should no longer be used
- std::optional for parameters and return types
- override, = default, = delete, and if absolutely needed final
Out of scopeI think we also have to be careful what
not to include. In particular, we should not have the mindset "this feature is new, so we have to use it", but rather evaluate on a case-to-case basis, if a new C++ language/library feature actually fits in the scope.
Some things I'm thinking of that should be used with care:
- Type inference. auto is nice for iterators, but using it for ints and vectors may easily make code less readable. Same for inferred return types -- apart from readability and increased compile time, they can also introduce subtle bugs/breaking changes if a return expression changes type by accident.
- Template metaprogramming. Variadic templates, folding expressions, variable templates, and all the dark magic around it. If it allows something to be used much easier, why not, but it shouldn't be the primary API design tool ;)
- Uniform initialization. This is the biggest misnomer and in my opinion one of the worst additions to the language. Instead of making anything uniform, now we're left with 4 (!!) syntaxes to initialize a variable, and new ambiguities (std::vector<int>{2, 3}) are introduced. A strict convention which syntaxes to use would solve this problem.
- constexpr and compile-time computations. Those are almost always optimizations, and thus should have low priority compared to more important features. We need to keep in mind that adding the keyword to a majority of the public API also has a readability/usability cost, possibly for a small number of users truly benefitting. It's different for cases where it's easy to add and can significantly improve performance in the general case.