Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Nexus

Pages: [1] 2 3 ... 7
1
SFML development / Sensible use of certain C++17 features
« on: December 08, 2021, 11:06:29 am »
While doing the C++17 migration, it's probably good if we settle down on some conventions regarding the use of newer C++ features. A lot of features are intended for a specific purpose and highly useful within that scope, but not intended as a broadband catch-all measure to be thoughtlessly applied to all code.

I'd suggest to be pragmatic during the migration, which means: we avoid unnecessary changes to existing code where benefits are mostly theoretical, and we focus on C++17 features which directly benefit the user (the currently ongoing PRs are a good example of this! :) )



A non-exhaustive list + my opinions:


[[nodiscard]]

From this comment:

[[nodiscard]] is primarily useful to prevent mistakes in non-obvious cases where a return value could be accidentally ignored. For example:
sf::Image image;
image.loadFromFile(...);
// WARN: didn't check 'bool' return type!

Same for methods which could be interpreted in a different way due to English language ambiguity:
std::vector<T> container = ...;

container.empty(); // empty the container
// WARN: this is an adjective, not a verb!

In other words: "should the caller use the return value" is a necessary, but not sufficient criterion for  [[nodiscard]].

95% of all functions which have a return value intend that to be used. We don't want that attribute on functions like Sprite::getPosition() or Sound::isRelativeToListener(), even if they'd technically qualify. Why not?
  • It makes APIs less readable, making function signatures more complex without adding meaningful information
  • The cases where where [[nodiscard]] is justifiably used are no longer recognized as such, thus the attribute loses its intended meaning.
  • We should prevent likely errors while not sacrificing ergonomics for unlikely ones.


constexpr

See also this comment.

Similarly, we could apply constexpr to a ton of APIs. But it comes again at a cost:
  • more complex API signatures
  • the necessity to move implementation to the header (which can in turn require exposing dependencies in headers)
  • restricted freedom in implementation (the function can no longer just compute values at runtime, cache them, or similar) -- removing constexpr becomes a breaking change
We should probably focus constexpr on value-like classes which are likely benefitting from being compile-time computable (or at least declarable), such as sf::Color.


noexcept

Primarily useful on move constructors, swap, destructors.
Benefit outside such special methods is questionable in SFML.


explicit

Useful for constructors that can act as conversion constructors (callable with 1 argument) or conversion operators.


Uniform initialization

int value = 3;    // (a)
int value(3);     // (b)
int value{3};     // (c)
int value = {3};  // (d)

SFML currently uses (a) for implicit conversions or single-value initializations and (b) for constructor calls:
int value = 3;
sf::Color a = sf::Color::Red;
sf::Color b(255, 0, 0);

I think we should stay with this convention, this also reduces amount of code to be changed for cosmetic reasons.

What we can do however is:
MyCStruct aggregate = {};
std::vector<int> v = {1, 2, 3}; // std::initializer_list, not uniform initialization



Type inference (auto + decltype)

We should use auto where it helps increase readability. Iterator declarations are such an example, also repetition of type names are potential candidates:
auto red = sf::Color::Red;
// instead of
sf::Color red = sf::Color::Red;

I would not use it where the type information helps the understanding of the code, or in function signatures or return types. It's also possible to introduce bugs by propagating a wrong type through multiple type-inferred functions.

decltype is typically used in generic code when it's hard to deduce the type of an expression. I don't think SFML has many places where it's needed.



Emplacement

Good if heavy copies can be avoided (moving SFML resource classes into a container). Otherwise, similar thoughts as in type inference section apply: it can make some code harder to understand, while not making a notable difference (due to optimization of temporary objects, or simply small enough sizes). Depends a bit on the context, good variable names can also mitigate that.

We should particularly avoid emplace_back(), when push_back() does the job (that will just cause an extra copy constructor call).


Trailing return types

In my opinion only useful when the return type depends on the parameter types. As such, we should limit their use to generic code which truly needs them.

2
SFML projects / Cinnamon Warfare
« on: December 25, 2015, 04:25:37 pm »
Cinnamon Warfare

[Download link]

This game is the result of an overnight contest in 2015, one year after In Soviet Russia. The topic this time was winter dream. We were a team of four, with quite talented sound and graphics artists. As usual, we tried to come up with something funny and a bit ridiculous :D

As a result of abysmal working conditions, the Snowmen's Labor Union declared strike. Snowmen equipped with jetpacks have coordinated an attack to steal Santa Claus' favorite underpants, in an attempt to blackmail him.

Load the Cinnamon Star Cannon and launch Santa's Personal Rocket to build a defense wall made of gingerbreads. Coordinate as a team of two and defeat the revolt! Only the upper sector is heated, make sure the Santa Rocket stays inside. A circle of lamps indicates your hitpoints.


The game needs two players (hot-seat); your goal is to survive as many waves as possible. Yes, I'm aware the gameplay is a bit confusing at first ;)



We developed it again with SFML and Thor. A Linux version will follow when I get to it.

Merry Christmas! :)

3
General discussions / Logic error handling in SFML
« on: November 29, 2015, 07:53:58 pm »
Since the topic reoccurs at every second pull request, it's time to discuss a library-wide best practice to handle logic errors, rather than a different one for every use case.

First, it is crucial to get the terminology right. Logic errors do not appear in correct programs. They are developer mistakes, such as out-of-bound access or division by zero. These can be prevented and are fundamentally different from runtime errors such as resource not found, which developers have to be prepared for. There are border cases, for example when user input is directly forwarded to the library, or when the error check can easily be done by the library itself and is expensive outside. But let's focus on the unambiguous cases for now.

Second, we're considering SFML 2 here, so exceptions are out of question.

There are several aspects I consider fundamental in the treatment of logic errors.
  • The handling of logic errors should not affect the semantics or the flow of the program. Since logic errors are bugs, they cannot be handled meaningfully at runtime. Their presence indicates a mistake in the program logic, and further execution of the program is unsafe, as it is based on erroneous code.
  • So why even bother at all? The idea is to prevent common mistakes and help the developer find them more easily. It's thus a pure quality-of-implementation (QoI) feature and not part of the API. As such, the extent to which assisting measures are provided is left to the implementation and need not be documented.
  • We should not punish developers who know how to write their code correctly. In particular, the goal is to have zero performance or memory overhead in Release mode.
  • Logic errors must not propagate through the program at all costs. The most difficult-to-track bugs are those appearing in a program part completely unrelated to the error source. Consequently, we should report logic errors as soon as possible. Ideally, running debuggers are interrupted immediately.
  • Logic errors must not be hidden at all costs. Related to the last point, a library should not aim to "fix" a user's errors by making assumptions on what he might have meant. An API exists to be used correctly, and mistakes should be reported rather than hidden, to make users aware and explicitly correct them.
C++ offers a feature that is tailored precisely to address all those points: Assertions. In my opinion, SFML should make use of them where appropriate.

The difficult part is to not overuse them and to avoid cluttering the code with loads of precondition checks. I think we should focus on mistakes that are 1. likely to happen and 2. not already caught anyway (e.g. by underlying STL data structures), to achieve a good trade-off between code clarity and error checks.

4
General discussions / More sophisticated logging and error reporting
« on: November 02, 2015, 09:00:36 am »
In the past, we've noticed that the current logging/reporting mechanism through sf::err() may not be flexible enough, e.g. here or here.

Possible features that can be discussed:
  • Division into different severity levels: error, warning, info, debug
  • Filtering (by level, or even module/functionality-wise)
  • Choosing the output streams (not only std::cerr)
  • Meta-information: time, source file, line
  • Ideally zero performance overhead for loggers disabled at compile time
For example, I could imagine a macro which creates an object with overloaded operator<<, that logs messages according to the current log configuration. Having a macro allows the collection of above-mentioned meta-information and complete away-optimization for statically disabled loggers.
SFML_LOG_E("An error with number " << 77)

Note that this thread is not about error handling, but logging. Whether to use boolean return types or exceptions is therefore not the topic here.

5
Feature requests / Output streams
« on: November 01, 2015, 04:31:33 pm »
Out of this GitHub discussion, the idea of output streams -- which has been around already for a longer time -- has been revived.

Basically, the idea is to have a class with the following synopsis, which acts as a counterpart to sf::InputStream and can be used to save resources to custom destinations -- files, memory buffers, archives, network sockets, ...
class OutputStream
{
    virtual ~OutputStream();

    // return if successful (or should it be bytes written and -1 on error?)
    virtual bool write(const void* data, Int64 size) = 0;
};


Now, Laurent and I have discussed a bit about the semantics of the write() function. Here's a summary:
Quote from: Laurent
We should be consistent with sf::InputStream [i.e. return the bytes written, or -1 on errors].
Quote from: Nexus
What would the caller of write() (within SFML) do if not all bytes have been written? Call the function again with the rest of the buffer?
Quote from: Laurent
That. For example, we can imagine a buffered stream that rejects data if there's no more room in its internal buffer (concrete example: a stream that wraps a TCP socket).

But that means that every place that calls OutputStream::write() must be prepared to handle that (or we write some kind of abstraction). And does it make sense to just call the function immediately again, i.e. in a loop? If an output stream fails to process all bytes at once, why would it be able to do so one microsecond later? Or how can the call site meaningfully determine how to react in such cases?

I'm just thinking that if this bytes written return value exists purely for buffering purposes, it had better be implemented within the concrete `OutputStream` implementation itself, which knows the specific scenario (ideal buffer size, how to handle errors, etc.)

Maybe I'm also just thinking of the wrong use cases :)

6
SFML projects / In Soviet Russia
« on: October 25, 2015, 03:22:52 pm »
In Soviet Russia

[Download link]

This project has already been on my homepage for quite a while, but I have never presented it here. It's a little game that I have developed with three guys in an overnight contest at a game developer's conference last year (Devmania 2014). The topic was meme, we tried hard to come up with something as ridiculous as possible :D

Russia with its long and diverse history has been the focus of many games -- it offers a lot of storytelling potential. Not so In Soviet Russia: this crazy little game lets you steer a snowboarding stick figure through the depths of Siberia, in a hopeless attempt to evade the trolls and endure the unnerving soundtrack. At least, things get a bit more colorful when you collect some rainbows to jump (with space). Move with left/right arrow keys. And if you really need to, brake with the down arrow.





The technologies we used were SFML and Thor. Currently the game is available for Windows, if I find the time I'll try to compile it for Linux too (but I need to setup the OS again...). I was again at Devmania last weekend, where we've developed another game. I'll post the new project soon :)

7
General discussions / SFML 2.3.1 released!
« on: July 11, 2015, 09:32:15 am »
SFML 2.3.1

This patch release fixes several issues in SFML 2.3, mainly related to Android and Linux platforms:
  • Android: fixed failure/crash when loading audio files
  • Android: ensure existence of window before accessing its dimensions
  • Android: added API level checks
  • Android: added JNI/event handling code
  • Linux: spawn Resized events only when window size actually changes
  • Linux: fixed unhandled X event (SHAPE)
  • Linux: remap keyboard when user changes layout
  • Linux: fixed UB in ewmhSupported()
  • Better support for GL_EXT_texture_edge_clamp
The changelog is available here:
http://www.sfml-dev.org/changelog.php#sfml-2.3.1

8
Audio / Music::pause() threading behavior
« on: May 25, 2015, 06:29:18 pm »
I store several music themes in the same archive file and load them with sf::InputStream. As you know, sf::Music runs in its own thread which constantly invokes the stream's methods to read new chunks of data.

No two music themes are playing concurrently, as that would lead to race conditions in the file stream. However, some themes remain paused while others play -- and this is already enough to trigger race conditions. Other than SoundStream::stop(), SoundStream::pause() does not wait for the thread to finish reading its current chunk. So, what essentially happens is that the old (paused) music is still reading while the new (playing) one also starts reading.

I'm wondering whether pause() should be blocking like stop(). It would be more difficult to implement however, as the thread continues running in pause status, so we can't just call thread.wait().

What do you think? With the current SFML implementation I see only two options, both of which are rather hacks:
  • Invoke SoundStream::getStatus() repeatedly until it's set to Paused (spin lock style). Apart from burning CPU resources, this approach is very sensitive to changes in the implementation: if we decide to cache the status and already set it in pause(), the assumption will break. I'm not even sure if it works at the moment.
  • Instead of pause() + play(), call stop() + play() + setPlayingOffset(). This is also wasting resources because it unnecessarily performs the thread destruction/construction and the streaming intialization.

9
General discussions / Shader uniform API -- part II
« on: May 13, 2015, 11:13:49 am »
In part I, we discussed possible APIs to provide more functions to set GLSL uniform variables from SFML, which is currently done by several sf::Shader::setParameter() overloads. I continued working on that issue and came up with two possible implementations. Both are available in the feature/shader_uniforms branch on GitHub. It's a proof-of-concept rather than a finished implementation.

The first option is to simply provide more overloads of setParameter().
Commit 8af7af4
(click to show/hide)

The second option would be to provide named functions.
Commit 7d1d4da
(click to show/hide)


Advantages of expressive API (setFloat etc.)

1. It avoids subtle type errors arising from implicit conversions (e.g., you can pass an int if you actually mean unsigned int, calling the wrong OpenGL function).

2. The call is expressive about the GLSL type being set. This is the strongest argument, as it makes clear how different C++ types like sf::Transform, sf::Color, sf::Vector2f are mapped to their GLSL counterparts mat4, vec4, vec2. Furthermore, it allows multiple mappings, for example sf::Transform can be passed as either mat3 or mat4.

3. We do not need to introduce new types like sf::Shader::Matrix3, as the function name would contain the name. However, this applies only to single values, not arrays. For example, it's not possible to pass an array of 4-dimensional vectors (unless types are introduced, or we assume a contiguous XYZWXYZW... memory layout).


Advantages of existing API (setParameter overloads)

1. sf::Shader::setParameter() need not be deprecated. In my opinion however, "setParameter" is not an ideal name anyway, "setUniform" would be much clearer. Everybody using shaders knows GLSL and in that context, "uniform" is a very clear and descriptive term.

2. Overloads of the same function allow for generic programming. And this is not an academic argument, it would allow a high-level representation of GLSL structs on C++ side:
GlslStruct st("light");
st.member("position", x, y);
st.member("intensity", 30.f);
st.apply(shader);
This is trivial to implement with setParameter overloads (I have already done it); but very tedious with setFloat, setVec2 etc. You essentially need to duplicate all the combinations.

It should be noted that setParameter overloads do not really simplify a lot on developer side. In SFML, we can't use templates, not even for GLSL arrays, because every C++ type has to be dispatched to the right OpenGL function that sets the uniform variable. However, as noted above, the SFML user can benefit from templates.

10
General discussions / 50% Discount on SFML e-books!
« on: May 05, 2015, 06:31:48 pm »
The Packt Publishing company offers the SFML community a discount on all e-books related to SFML.
If you order an e-book via Packt website, you can enter the following code to get a 50% discount:

sfgde50

The code is limited to 500 uses and expires 31st of July 2015, so if you're interested, make sure to order soon! :)

11
Graphics / Bounding rectangle of transformed shapes
« on: May 02, 2015, 04:43:42 pm »
What does getGlobalBounds() return? As a user, I would clearly expect the minimal AABB that covers the area of the underlying geometry.

In order to obtain the global bounds, SFML transforms the local bounds, and then computes the bounding rect of those transformed local bounds. Now, for sf::Sprite which has a rectangular area, this works fine. For rotated sf::Shapes however, this yields rectangles which are considerably larger than necessary. The current behavior relies on a technical implementation detail and makes the shape's global bounds for all practical purposes (like simple collision detection, mouse clicking etc) useless.

I suggest adjusting sf::Shape::getGlobalBounds() in order to return the minimal bounding rectangle. I can create a pull request if you agree.

12
Graphics / Subtle positioning behavior
« on: April 30, 2015, 10:57:21 am »
First, this is not a SFML bug, yet behavior that one would probably not expect as a user. I stumbled upon it yesterday, after debugging almost an hour and searching in the wrong places :P

There is a bug in the following code, leading to a rendering that doesn't appear smooth when the sprites move.
// Two sprites. Assume they're initialized with texture and everything.
// Origin is (0, 0) and rotation is 0 at all times.
sf::Sprite sprite;
sf::Sprite sprite2;

// Align sprite to integral coordinates (pixel-perfect rendering)
sprite.setPosition(std::round(...), std::round(...));

// Second sprite starts at the center of first.
// Also round sprite2 for pixel-perfect rendering
sf::FloatRect bounds = sprite.getGlobalBounds();
sf::Vector2f center(
        bounds.left + std::round(bounds.width / 2.f),
        bounds.top + std::round(bounds.height / 2.f));
sprite2.setPosition(center);

Does anybody spot it? ;)

13
SFML development / API deprecation model
« on: April 07, 2015, 11:34:22 pm »
With sf::Event::MouseWheelMoved, SFML starts to mark parts of its API as deprecated. Deprecated features will continue to work in existing code, but their use is discouraged in new code. These features will be removed in the next major version, that is, SFML 3.

The library should encourage a fast transition from deprecated features to their superior counterparts. At the moment, it does so using annotations in the API documentation. However, this is not enough, as people used to a feature will continue to use it, without consulting the documentation again. Expecting people to constantly look up the documentation for working code is naive; nobody does that. At best, a small fraction of people has a look at Doxygen's deprecation list from time to time.

That's why I think it would be nice to have a stronger mechanism to make people aware when they use deprecated features. I have several ideas in mind, for each build step:
  • Compile time: There are compiler-specific keywords (__declspec(deprecated) and #pragma deprecated in MSVC, __attribute__((deprecated)) in g++), however their use is limited to some C++ identifiers (mainly functions). While this is not relevant at the time being, C++14 introduces the [[deprecated]] attribute as a general-purpose and portable feature.
  • Preprocessor time: A possible addition would be a macro SFML_NO_DEPRECATED for conditional compilation, in which all the deprecated features are disabled. This sounds very good, but there is one problem: conditionally compiled variants of the library lead to different binaries -- already because the symbol in question is no longer exported in dynamic libraries. If we consider how many people even get the simple SFML_STATIC wrong, I don't want to imagine how many people encounter subtle linker or even runtime errors in this case.
  • Runtime: The sf::err() stream could be used to emit warnings when deprecated features are used. This requires additional code in corresponding functions as well as a mechanism to enable/disable the warnings. It detects the deprecation only late (possibly never), but is binary compatible.
What do you think? Before you argue from your own standpoint as an occasional SFML user and say "it's exaggerated", consider that the whole point of using deprecation in the first place is to avoid API-breaking changes caused by removal of functionality. SFML has not introduced deprecation primarily for occasional projects which can be adapted in 15 minutes. It's rather something that concerns long-term projects, potentially commercial -- these projects are the ones that benefit most from early recognition of deprecated features.

14
SFML wiki / List of dependencies
« on: March 29, 2015, 11:52:51 pm »
Since the SFML dependencies have changed a lot recently (especially with XCB), it  would be nice to gather a list of required libraries on Linux. Finding out the package names can be tedious at times, and it's not necessary that every user does so again and again. I've written a small script for Ubuntu, but I've only added entries over time. Some (e.g. X11 ones) might not be needed anymore... And depending on the distribution, there can also be differences.

Here is the link. Please feel free to improve it where possible.

Maybe the page can be extended for Windows and static linking.

15
General / Problem finding correct libjpeg
« on: March 23, 2015, 02:18:48 pm »
When I tried to compile Thor with MinGW, I got linker errors in one of SFML's dependencies. Some symbols from libjpeg were not found.

Debugging showed me that during the CMake configuration step, a wrong libjpeg path is found:
find_package(SFML 2 COMPONENTS audio graphics window system)
message(STATUS "Dep: ${SFML_DEPENDENCIES}")
Quote
Dep: ... C:/Program Files (x86)/Java/jre7/bin/jpeg.dll ...

Why does it even find a .dll and not a .lib file? These arguments are supposed to be passed to target_link_libraries in order to be linked (not loaded at runtime like DLLs).

Investigating FindSFML.cmake shows that the call to find_library (through the macro find_sfml_dependency) already returns this wrong path... Which is strange since the library path appears first in the list, and I made sure libjpeg.a is there.

Debugging output used in FindSFML.cmake inside find_sfml_dependency:
message("\nFinding ${output} (${ARGN}) in ${FIND_SFML_PATHS}")
message(" -> ${${output}}")
Quote
Finding JPEG_LIBRARY (jpeg) in C:/C++Libs/mingw;~/Library/Frameworks;/Library/Frameworks;/usr/local;/usr;/sw;/opt/local;/opt/csw;/opt
 -> C:/Program Files (x86)/Java/jre7/bin/jpeg.dll
There is a file C:/C++Libs/mingw/lib/libjpeg.a, but somehow it's not recognized.

Strangely, the NMake toolchain works fine.

By the way, why does SFML not ship a FindJPEG.cmake module?

Pages: [1] 2 3 ... 7