[…]
Interestingly, by ‘shifting responsibilities’, I meant from the
user to the library developer, not compiler to run-time.
That may or may not be the same thing; they’re more or less
orthogonal…
I really don’t know what you mean here - if they’re more-or-less
orthogonal, how can they be the same thing?
Sorry, what I meant was that when it comes to implementation, one may
bring the other, though the concepts are theoretically orthogonal.
For example, you can shift the responsibility of chosing the
correct “destructor” into a library by having a generic Destroy()
function that takes a void * or similar argument - but that means you
have to use some kind of object model that allows the Destroy()
implementation to figure out who to call to get the job done.
[…]
When I said ‘a structure requiring a deep copy…’, I meant:
‘where obtaining a separate, independent copy of a structure and its
data requires a deep copy to be performed (where a = b is not
sufficient; where a = b is semantically incorrect).’
Well, that’s actually a pretty hard problem in real applications. How
deep is a deep copy…? Maybe you need refcounting and copy-on-write
for performance reasons, or maybe that would just be a waste of
cycles?
This is something that has to be decided on the
application/engine/higher level library level, and I think building
that sort of stuff into a low level API like SDL would only confuse
matters and make it harder to use properly.
Basically, when it’s not clear what the correct way of doing something
is, it’s better to leave the decision to the application code.
[…]
Personally, I dislike APIs that try to wrap things like this. C++
APIs that “abuse” operator overloading to do this are even worse.
It can be “handy” and (in the case of C++) make the code look
cleaner, but for the most part, it just means you have to double
check everything all the time to be sure what’s actually going on.
Is that really the case? The reason I chose SDL_Surface as an
example, is that it’s not immediately obvious how to clone it just
by looking at a list of functions (okay, could be ‘fixed’ by
documentation, but surely that is the definition of ‘less
intuitive’).
Well, if you step back and look at the problem: When do you want to
clone an SDL_Surface? If you ever want to do it, why?
It turns out that it’s pretty much a non-issue, because the few times
you actually do want a copy, you actually want a slightly different
version, such as a version suitable for fast blitting to the screen
(SDL_DisplayFormat*()), or a temporary version in some specific,
handy format for some direct pixel level manipulation.
Also, the documentation states:
/* This structure also contains private fields not shown here
*/
So it is at least semi-opaque already. Why not define the API to
include functions for querying the (almost exclusively) read-only
data?
Because it’s just more typing, and doesn’t really prevent anything but
silly typos…?
If the documentation states that these fields are read-only, that
pretty much covers it, IMHO.
Granted, it may not be quite as nice to write:
SDL_Surface_width(surface); (1)
as it is:
surface->w; (2)
But it would prevent a user from doing:
surface->w = 100; /* Semantically incorrect */
Maybe I’m a superhuman coder or something, but I don’t think I’ve ever
made that particular mistake, actually.
And it does, at least to me, seem 100% more obvious what surface
refers to in (1) when compared to (2). Again, the majority of
overhead of development is in maintenance, so the pain of typing a
really verbose statement, like (1) is surely off-set by the saving
you’ll get when the code is re-visited?
You’re forgetting that SDL is a pretty low level API, that is
sometimes getting dangerously close to the dreaded “inner loops” of
performance critical software. Turning raw field accesses into
function calls - or even inlines that can potentially confuse the
optimizer - needs to be strongly motivated.
If you want it all nice and fool-proof, C is not for you, and nor is
the raw SDL API. There are various real high level language bindings
for SDL (including work-in-progress EEL/EELBox) that do a much better
job of this than C or C++ ever could. They’re not as fast as C or
C++, but the difference tends to be irrelevant, unless you’re working
on handheld devices and/or doing rather low level work. Even when you
do need the best raw performance you can get, these tools can save
a great deal of time by allowing you to prototype and get the
algorithms right before you start hacking C code.
[…]
I have been led to believe that information hiding is a Good
Thing.
It is, generally, but I don’t see how trying to hide fundamental
language constructs like pointers can ever be a Good Thing. The
correct solution to that problem is using a higher level language
that avoids these constructs entirely.
Well, given that pointer abuse remains one of the most common causes
of errors (OMFG, you should see some of the code I have to maintain
at work!), and that almost every language since C has tried to do
exactly that (even C++), I can’t see how ‘hiding pointers’ can be
called a Bad Thing. That is to say, if you don’t need to expose a
pointer, what’s the point (pun intended) of putting power and
temptation into the hands of a user?
How do you actually hide pointers when dealing with a C API in C?
(Yeah, I know it’s possible, and I’ve even tried it in some of my
older APIs - but it tends to cause more problems than it solves.)
IMHO, the correct answer is: Pick a nice high level language without
pointers, and make a binding for it.
Or lets look at it another way… SDL isn’t used ‘just’ in C
programs - it has bindings for many other languages, some of which
don’t even HAVE the concept of a pointer.
Exactly.
[…cloning a surface…]
But what if you DO need to do it? Granted, one can clone a surface
using:
new_surface = SDL_CreateRGBSurface(old_surface->flags,
old_surface->w, old_surface->h,
old_surface->format->BitsPerPixel,
old_surface->format->Rmask,
old_surface->format->Gmask,
old_surface->format->Bmask,
old_surface->format->Amask
);
SDL_BlitSurface(new_surface, NULL, old_surface, NULL);
But that seems a little obtuse to me.
Actually, I’m not even sure if that handles every surface correctly -
so yes, you have a point there.
new_surface = SDL_CloneRGBSurface(old_surface);
Would do the same job, except neater. It would also prevent people
trying to copy the structure manually, as shown in the
documentation.
So, an add-on library with handy tools like that would be a nice thing
to have…
[…]
The issue, in my view, is that C lacks explicit support for it.
Can this be worked-around in a way that users find easy to
understand, use, etc?
Well, you can make all structs “private” and provide function
calls to access the information instead, but all you gain from
that is some minor extra freedom in changing SDL internals without
breaking source and/or binary compatibility with applications, at
the cost of potentially significant overhead.
Yes, that’s the problem isn’t it… this is basically the problem
Stroustrup had when he first started to develop C++; people started
to circumvent the protection mechanism in order to avoid the
overhead of a function call, just to get at a member value, such as
an int.
It’s a problem C was never designed to solve, so it’s kind of hard to
solve it in C without adding overhead or making things cumbersome…
I just don’t think it’s worth the effort.
Surely, the goal of a library provider, is to do exactly that;
provide an interface that is stable, that a user can work to with
certainty, while allowing the library developer as much freedom as
possible to change the implementation details?
Sure, but how far can we afford to take it? There’s no such thing as a
free lunch.
Much of the point with SDL is exactly to allow low level access to
things, and doing that while at the same time being open to arbitrary
pixel formats, weird backends and whatnot requires an incredibly
complex API - or one that’s simple but generates lots of data
conversion overhead.
I mean, you could just hide all the raw pixel data details and provide
SDL_SetPixel() and SDL_GetPixel() calls with 32 bits per channel, and
cover pretty much everything. However, any applications doing pixel
level software rendering would invariably be dog slow, unless they
could bypass that - and if they could, we’d be back at square one
again, exposing “internals” in the API.
SDL strikes a pretty good balance here. It supports most pixel formats
of interest, it supports most kinds of backends (software and
accelerated) pretty well, and it provides a reasonably clean and
simple API that allows enough low level access for most things you
ever want to do in a portable multimedia application.
[…]
Incidentally, I’m not talking about all structs, for example,
SDL_Rect is simple (and small) enough to be perfectly understandable
and safe as it is, making it opaque would just be silly.
SDL_Surface, on the other hand, has up to 3 levels of indirection
(Surface->PixFormat->Palette->Color) and that’s just in the public
part!
You need that information exposed anyway, to be able to do pixel level
access from application code. Maybe you never need that, and maybe
SDL just isn’t the right tool for the jobs you do?
[…]
What are peoples experiences with it?
My experience is that overdoing this just leads to
overcomplicated, inefficient APIs for no real gain whatsoever.
IMHO, the SDL API is a brilliant example of how to do it right.
It’s lean and mean, easy to learn, efficient, and still allows
backends enough slack to do some pretty odd things. (Think
glSDL…)
I’m not saying it doesn’t. I am asking if it is perfect and if
it isn’t, how can I help to improve it?
Well, nothing is perfect, but that’s life.
There are various issues that people are complaining about from time
to time, but I think most of these have been dealt with in SDL 1.3
already. Unfortunately, I’ve been out of the loop for too long to say
anything about the details at this point.
//David Olofson - Programmer, Composer, Open Source Advocate
.------- http://olofson.net - Games, SDL examples -------.
| http://zeespace.net - 2.5D rendering engine |
| http://audiality.org - Music/audio engine |
| http://eel.olofson.net - Real time scripting |
’-- http://www.reologica.se - Rheology instrumentation --'On Saturday 19 July 2008, Eddy Cullen wrote: