[SDL] SDL 2.0: Evaluating API Usability (Long)
Brian
brian.ripoff at gmail.com
Mon Mar 10 11:01:08 PDT 2008
Hi.
If I have left anything out, feel free to let me know. I apologise in
advance if I rambled a bit, or went slightly off topic.
>> --------------
>> CLASSIFICATION
>> --------------
DEGREE
PART-PROFESSIONAL
>> --------------
>> QUESTIONS
>> --------------
>> 1. What do you like about the presentation of the SDL APIs? Which tasks
>> do you find particularly easy, straightforward or simple? Which APIs do
>> you feel work really well for the task they are intended for?
SDL is simple. It is easy enought that I can remember most of the API
calls and arguments from memory. The functions are simple enough that
most of the time one doesn't need to look beyond the function
prototype itself for documentation. I think its wide user base attests
to how much it gets right in its API.
>> 2. What DON'T you like about the presentation of the SDL APIs? Which
>> task do you find particularly difficult, awkward or confusing? Which
>> APIs do you feel make the task more difficult? What are your pet hates?
Const correctness, or lack thereof, is annoying. C++ code is more
prone to using const a lot (because member functions can be marked
const) which occasionally means one must make unsightly const_casts to
overcome the API. The functions (I can't remember the specific ones)
logically shouldn't be const. In addition, there are some functions
(SDL_BlitSurface) that would appear to be const-incorrect, but in fact
need non-const pointers. Conceptually, blitting between two surfaces
would leave the source unchanged, yet the source is non const (I
remember going through the SDL source at one stage to figure out if
there was a reason, if memory serves there was one). Then again, the
source rectangle is not modified, but is not marked const.
SDL_ListModes is evil. I dislike casting a pointer to -1 to check for
a condition. An additional API call would be preferred:
if( !SDL_AnyVideoMode() )
{
// use SDL_ListModes()
}
I'm not sure if they've fixed this, but when you call SDL_SetVideoMode
more than once (on Windows at least), and you have an OpenGL context
on it, the opengl context is lost (along with textures, shaders etc
you have stored in the context). This is annoying, because AFAIK it is
possible under Windows to resize a window without destroying the
context. Another annoyance is (again, under Windows at least) if you
hold on the window title bar, your program's main thread seems to
freeze. I don't know if this is caused by how Windows handles it.
The lack of window APIs to discover the screens current resolution and
or position the SDL window (environment variables are a poor API
choice, IMHO). I know SDL 1.3/2.0 is supposed to address this.
void SDL_ToggleFullscreen() seems rather pointless given that it
doesn't work reliably in a cross platform manner. A better alternative
is one like:
SDL_Surface *SDL_ToggleFullscreen(), so it could be implemented under
all platforms.
More consistency in event enums to event types. For example,
SDL_ResizeEvent uses the enum SDL_VIDEORESIZE and SDL_ExposeEvent uses
SDL_VIDEOEXPOSE. I never can remember whether to check if(event.type
== SDL_EXPOSEEVENT) or to cast SDL_VideoExpose &expose = event.expose.
SDL has an implementation of a thread safe queue, it would be
fantastic if it exposed that as a type for people to use for
inter-thread-communication. At the moment, you can have number of
source threads pushing events towards one event sink, but some of my
programs require bi-directional inter-thread messages, and a queue is
one of te simplest and easiest ways to do this.
SDL_UserEvents are great, but it is difficult for an extension library
writer (example: Bob Pendleton's NET2 library) to ensure that a given
event.user.code isn't being used by the library user, or by another
extension library. SDL should expose a way to allocate event.user.code
values (or ranges), and deallocate them:
int base_user_code = 0;
Net2_Init()
{
base_user_code = SDL_AllocUserEventRange(10); // 10 distinct user
code types
}
Net2_Quit()
{
SDL_FreeUserEventRange(base_user_code);
}
Alternatively, SDL could only allow a single user event code type per
library, and leave it up to the library writer to provide an
additional enumeration in their event.user.data1 or data2 structure
pointers. Still, there should be some way to reserve certain code
values in advance to ensure that at run time each event code maps to a
unique event type.
>> 3. How do you find error handling? What do you like about it? What don't
>> you like about it? Are there particular examples of error handling in
>> other APIs that you prefer? Why?
The error handling is, for most uses, simple and easy. By having a
consistent usage, functions returning pointers return NULL on failure,
all other functions return negative numbers on failure. This leaves
scope for functions returning non boolean errors (example:
SDL_BlitSurface() returning -2 signals a special, recoverable
failure). It also means that to catch all errors on a SDL function
Foo, one writes if( SDL_Foo() < 0 ) { /* handling */ }, which is easy
to remember. Looking up the source, it appears to be thread safe, an
extra bounus. It would be nice to have more symbolic error constants
though, AFAIK there is no #define for the -2 value returned from
SDL_BlitSurface.
>> 4. If you use / have used SDL with C++, please answer the following:
>> a. Do you wrap SDL in C++ class(es)?
>> - If not, why not?
>> - If so, what difficulties / gotchas have you
encountered? Which APIs
>> have been particularly troublesome?
a) Yes, mostly. It was quite easy to write RAII wrappers around the
majority of SDL (well, any function pair that allocates and
deallocates memory, not much point wrapping the others). Trouble was
caused by the threading API, as detailed below.
>> b. Have you tried using exceptions in your SDL code? Which approach(es)
>> have you used? Did you give-up and go back to traditional (C-style)
>> error handling and if so, why?
b) Yes. I'm not sure what you mean by "approach" here, I just made it
so that the RAII wrapper constructor for a given class would either
allocate successfully, or throw an exception (usually
std::runtime_error unless I wanted to handle that error differently,
i.e. file not found) which usually just contained the SDL_GetError()
string.
However, I am currently having trouble with the threading API. I am
writing a multi-threaded network program, and most network errors (in
this program, I do not attempt recovery in the event of network
failure) propagate to the thread function (the one that gets passed to
SDL_CreateThread). This function has a try...catch block in it,
catching std::exception instances and also a general catch statement
(catch(...)). However, when ever an exception is thrown the program
crashes hard with an access violation. Debugging this is difficult (I
suspect the error is caused during stack unwinding). Because I am
paranoid about wrapping pointers in smart pointers or RAII wrappers of
different kinds, I don't think that this error is caused by my code,
then again I have found SDL to be quite stable, so I am still 50/50
whether it is SDL or me that is causing this problem. I am tempted to
try convert my program to traditional C error handling, to see if the
problem persists, but that is a lot of work. I am loath to blame SDL
because in my experience I am always to blame, but I am quite
experienced with C++ and I have yet to find anything wrong that I have
done that could have caused this.
>> 5. Any other comments that do not fit the above.
SDL is fantastic :)
>> --------------
>> END
>> --------------
>>
>> Legal Bit (Actually REALLY IMPORTANT!)
>> =========
>>
>> It is my intention to use any comments made in relation to this post as
>> part of my research, for which, the copyright will be held by myself
>> and/or University of Southampton (UK).
>>
>> Data Protection Act 1998. Any personal data collected will be used
>> exclusively in connection with this research project. The originating
>> email address will be stored purely in order to facilitate the collation
>> of the comments made by an individual (e.g. to remove duplicates) and
>> for direct email communication by me in order to clarify any comments.
>> The final published data will not contain any information that can be
>> used to identify individual contributors.
>>
>> For the avoidance of doubt, please email your response to me directly
>> (CC: will be fine). I will take this as your acknowledgement and
>> confirmation of consent. NOTE: If you fail to do this, I will NOT be
>> able to use your contribution!
More information about the SDL
mailing list