SFML community forums

Help => General => Topic started by: eXpl0it3r on September 18, 2013, 04:26:19 pm

Title: SFML Game Development - Use of assert in resource holder
Post by: eXpl0it3r on September 18, 2013, 04:26:19 pm
So I've been reading parts of the book on my vacation and I've noticed a little issue. It's more a question of deign than an actual bug. The implementation of the resource holder makes use of assert, which is great and it's explained quite nice, but the issue is, that there's no way to prevent stepping into the assert, except by logical exclusion up on programming. As we probably all agree, we should write code that leaves as less doors for making mistakes as possible, thus having no way to check, whether a resource already exists from outside of the class, forces the user to either step into the assert or make unnecessary comments everywhere, which might get over read later on, to prevent the loading of two identical resources/with the same ID.

My conclusion is, that the resource holder class should have a exists() function to check the existence of resources, before loading them and praying it won't blow up in the assert. I mean one doesn't necessarily have to check the existence every time, but having no option to do so, seems rather dangerous/bad.

For examples:
TextureHolder textures;

if(!textures.exists(Textures::Airplane))
{
    std::vector<std::pair<Textures::ID, std::string>> textures_assets = load_textures_info_from_file();

    for(auto& asset : textures_assets)
    {
        if(!textures.exists(asset.first))
            textures.load(asset.first, asset.second);
    }
}
 

I know the other code looks prettier and one doesn't always have to check it if the logic allows it, but letting the user step into asserts because there's no way to check any kind of existence seems like a bad design.
Sure asserts are there for debugging, but you can't guarantee that all the possible scenarios will be played out, while running a debug build, thus releasing a game with possible bugs.

What do you guys think? Why didn't you implement such a function? :)

What about a separate forum for the book? :P
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Hiura on September 18, 2013, 04:42:24 pm
Assertions are nice for debugging and when designing a library, to make sure internal state are valid. But when there is an interface with the user code it's not the best tool. I would instead recommend exception or return code (for simpler case) instead.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Nexus on September 18, 2013, 04:48:33 pm
The philosophy behind ResourceHolder is quite simple: You load the resources initially and once, and you use them again and again. At the time you use the resources, you require them to be already loaded.

An exists() method provokes a different design: Users don't have a clear owner and scope of the resource anymore. That is, the ResourceHolder cannot guarantee the availability of resources, and code accessing becomes more complicated because it needs to check for the existence before using the resources.

I agree that more sophisticated scenarios such as loading on-demand require a more complex API. But exists() is only the first step towards it; eventually you want to hide the case differentiation and simply request the resource, be it allocated or not. As soon as you have this, you might also require the opposite, namely releasing a resource as soon as it is not used anymore. You will end up with a much more complex design, similar to thor::ResourceCache (http://www.bromeon.ch/libraries/thor/v2.0/doc/classthor_1_1_resource_cache.html).


Assertions are nice for debugging and when designing a library, to make sure internal state are valid. But when there is an interface with the user code it's not the best tool. I would instead recommend exception or return code (for simpler case).
Assertions are fine to check preconditions in functions. There's a long section in the book that explains exactly why we use assertions and not return types or exceptions ;)
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Hiura on September 18, 2013, 04:59:47 pm
It depends on the kind of preconditions. Quoting Andrew Koenig:

Quote
I believe, therefore, that when a program discovers something that is irrefutably wrong with its internal state, it is better off terminating at once, rather than giving its caller the opportunity to pretend that nothing is wrong.

If you like, I think that exceptions should be reserved for situations in which it is possible to do something sensible after catching the exception. When you discover a condition that you thought was impossible, it's hard to say much about what might happen afterward.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Tank on September 19, 2013, 08:26:51 am
Nexus, the philosophy is alright, but you still allow to get() a specific resource which carries an assert(), without a way to prevent trapping into it. This will only happen in typo scenarios mostly, but the interface still forces me to trial'n'error.

The developer is not able to guarantee that all preconditions are met before calling get() without remembering what gets loaded in own code.

An exception in this case would fit better in my opinion: The developer can't avoid the error case, thus you can't assert that the certain preconditions are met.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Tank on September 19, 2013, 08:37:51 am
Quote
Assertions are nice for debugging and when designing a library, to make sure internal state are valid. But when there is an interface with the user code it's not the best tool. I would instead recommend exception or return code (for simpler case) instead.
I do not agree at all. Personally I separate between programming errors and exceptions. A programming error for example is this:

resource_manager.get( "non_existant" );

If there's no resource with the ID "non_existant", and the resource manager provides me with a function to check for that resource prior getting it, then I've used the class wrong. This is a perfect case for assert(). (By the way: make sure to run your stuff without NDEBUG/asserts enabled!)

If I do this, however:

resource_manager.load( "cool_stuff.png" );

and the file can't be loaded, then it's an exception, because I can't guarantee beforehand that at the time the file is going to be read, it exists, is readable etc. Ergo an exception should be thrown.

Lastly, my opinion on returning error codes: Just don't do it. C++ provides us with exceptions, so I don't see a reason for using error codes. They require me to check them every time (I can simply overlook them) and don't scream when I forget to do so. They also go down the whole callstack, which is awesome for error handling delegation.

By the way, bool values are also some kind of error codes. Personally I'd design, for example, sf::Image::loadFromFile() to be a void function. Laurent, would you still follow the same way of designing the functions or choose another if you'd start now? I ask because I changed my way of doing this too at some time. ;)
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Ixrec on September 19, 2013, 08:52:32 am
I think returning booleans as error codes for functions like loadFromFile is much better than leaving them void.  With a function like that, there's such an obvious, intuitive, probable and easy to handle error case (the file can't be loaded) that any programmer using the function really should be expected to check the return value and have a contingency plan for when it fails in that obvious way.  If that was an exception instead, we'd merely be wrapping every loadFromFile in a try/catch block instead of an if(!) statement, and I'm pretty sure the latter is more intuitive, more concise, and probably less expensive to implement.

Of course most functions aren't like this, non-bool error codes are definitely bad, and asserts and exceptions should be used for the majority of error handling, but making loadFromFile return void instead of bool would just take away useful, easy-to-understand information for no good reason, in my opinion.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Laurent on September 19, 2013, 09:04:32 am
Actually, I'm already thinking about using exceptions in SFML 3.

I had two arguments in favor of error codes:
1. no error is fatal, SFML always manages to provide a valid fallback in case of error
2. exceptions are hard to translate to C (and don't talk about long jumps, I'm referring to design issues)

But:
1. displaying a white texture is not what the user wants, and doing it is much less helpful than throwing an exception with an explicit error message
2. if I manage to translate exceptions at the C level, then error handling will be much easier in bindings (many people try to catch sf::err() output from their binding to retrieve error messages, to no avail)
3. exceptions are really the way to go in C++
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Ixrec on September 19, 2013, 09:19:46 am
SFML 3 might throw an exception when textures go out of scope?

That sounds amazing.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Laurent on September 19, 2013, 09:22:44 am
No no no. This would be crazy, there's nothing wrong with going out of scope (every objects ends up being out of its scope at some point) ;)

SFML 3 will throw an exception on error instead of returning false. For example, if loadFromFile fails.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Nexus on September 19, 2013, 11:39:39 am
An exception in this case would fit better in my opinion: The developer can't avoid the error case, thus you can't assert that the certain preconditions are met.
load() throws an exception upon failure, because not being able to load a resource is a runtime error, which can indeed not be avoided. However, accessing a wrong resource is a logic error; it implies a bug in the program which can be avoided and which won't happen if the program is correctly written.

If that was an exception instead, we'd merely be wrapping every loadFromFile in a try/catch block instead of an if(!) statement, and I'm pretty sure the latter is more intuitive, more concise, and probably less expensive to implement.
That's exactly how you don't handle exceptions. Other than Java code, C++ code that uses exceptions typically has very few try-catch statements -- one at the top (e.g. in main()), and possibly some in places where you need exception translation, logging, or rollback semantics. The big advantage of exceptions is that you can keep code clean of error handling.

SFML 3 might throw an exception when textures go out of scope?

That sounds amazing.
Amazingly insane ;)

A destructor should never let an exception escape (GotW #47 (http://www.gotw.ca/gotw/047.htm)).
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Hiura on September 19, 2013, 11:47:26 am
However, accessing a wrong resource is a logic error; it implies a bug in the program and won't happen if the program is correctly written.

Not necessarily. If you have some config file to, well, configure your app and customize your images or anything else, you cannot be sure that it will always be valid.

Also, there are different kind of exceptions. For example std::logic_error, and many more (http://en.cppreference.com/w/cpp/error/exception).
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Nexus on September 19, 2013, 12:10:44 pm
That's right. But as mentioned previously, to cover all these cases, the design should be fundamentally different, a failing get() alone isn't enough. ResourceHolder is very simple in its implementation, yet covers the typical use cases (load resources initially, use them later). I think it's a good tradeoff between complexity and genericity. Like many techniques described in the book, you should not consider it the solution to every problem, but rather an introduction to the topic. Keep in mind that the book doesn't require previous game development knowledge, so we can't directly start with complex use cases such as config files, shared ownership or loading on-demand :)

In the C++ community, there has always been a controversy about the utility of std::logic_error and its derived classes. I personally tend to use assert to report logic errors, but there are clearly scenarios where exceptions make sense, most of which are related to user input that is directly forwarded. But some are really questionable, e.g. std::vector::at() which throws std::out_of_range. I've seen a lot of people use it in the expectation that it is somehow safer than operator[], but in the end, you sacrifice a lot of performance for an error which you can't meaningfully react to (other than terminating the program and writing a log, for example). The philosophy differs also strongly between languages, Java for example uses exceptions for everything, and even has strong exception specifications.
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Hiura on September 19, 2013, 01:25:32 pm
Quote
Keep in mind that the book doesn't require previous game development knowledge

My apologies, my answers were not really targeted to the book but more on general programming practice. If we were talking about a book about designing and building software, I stand on my position. But here it's indeed more «affordable» to use assertions, since its target audience is more beginners than professional.

Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Tank on September 19, 2013, 01:41:11 pm
Quote
However, accessing a wrong resource is a logic error; it implies a bug in the program which can be avoided and which won't happen if the program is correctly written.
I don't think that it can always be avoided if "correctly written". Let's say, for example, a library prepares a resource manager with some default resources. A newer version renames the identifiers, so calling get() with the old ones leads to an assertion failure. I have no way of avoiding programming errors.

And generally, using a container or similar for tracking what I've put into the resource manager and what not is no real option for me. ;) What exactly does ResourceManager::isLoaded() add to the complexity?

Granted, the examples/scenarios are often to be found in more complex source code, but in my opinion that's not a valid reason for not choosing a solution that's fine for every complexity. For me, having no way of avoiding trapping into an assert() is not the ideal code design.

I don't remember if it was Bjarne, Scott or Andrei, but I've been loving the quote since I found it: "Make it hard to use your code wrong." -- except I miss something like this: "..., but make it easy to use it right!". ;)
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Nexus on September 19, 2013, 02:15:10 pm
I can only repeat myself: I agree that there are more complex use cases, but I don't consider them relevant in the scope of the book's ResourceHolder. I don't see where we would benefit from a larger API.

That's what I meant with "An exists() method provokes a different design: Users don't have a clear owner and scope of the resource anymore." Instead of requiring the resource to be loaded before it's used, users are then allowed to query for the availability, which leads to a different usage pattern. They start to load resources in multiple places, differentiate cases and use delayed loading. This however works against the original ideas (for example centralized loading, simplest possible usage, clear ownership).

When talking about design, you really have to keep in mind that we're not presenting the ultimate solution, but trying to find a good tradeoff between simplicity, flexibility, and suitability to teach new concepts. Those of you who have the book, please read the section "A typical use case", it explains the reasoning which led to the ResourceHolder API as it is now. Keep also in mind that we emphasize multiple times that the reader should not see himself limited by the basis we provide; it is always possible to extend the given code :)
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Tank on September 19, 2013, 04:03:03 pm
Quote
When talking about design, you really have to keep in mind that we're not presenting the ultimate solution, but trying to find a good tradeoff between simplicity, flexibility, and suitability to teach new concepts.
I know, I just find it confusing to assert() and don't provide anything to avoid the trap. Adding exists() indeed changes how the resource holder is used, probably, but I see two options here:

1) If it makes using the class more secure, then it's probably the way to go. Personally I think exists() would not destroy any simplicity, flexibility or suitability.
2) Use find() instead of get(): The simple change of wording can result in a change of the return type, e.g. from Resource& to Resource*, thus indicating the possibility of a nullptr. In my opinion this is fine, because the word "find" includes finding nothing.

I'm fine with how it's written in the book, this is purely about principles and general code design. On the other side, my opinion is: Even if beginners are reading the book, there's nothing wrong showing what options you have and what might be a "better" solution, even at the cost of a little bit more complexity.

I'm actually happy that you guys are using assert(). ;)
Title: Re: SFML Game Development - Use of assert in resource holder
Post by: Ixrec on September 19, 2013, 09:46:21 pm
Just because my last post was so stupid the way I wrote it, I felt I should clarify that what I meant to say was "throw an exception when you try to use textures that have already gone out of scope".  Should've read that a few more times before posting.

And in retrospect you guys are right about making loadFromFile throw an exception instead; usually all I do after checking the bool is throw one myself.