A few comments and suggestions on Chris' C++ interface.
I think that not having object creation via "new"--as expressed
Sherief--is not the specific problem with the interface per see. Many
creational design patterns--e.g. Factory Method, and Abstract
Factory--create objects similar to Chris' interface to achieve exactly
the implementation encapsulation that he is going for. In fact
alutCreateStreamFromFile can be considered a Factory Method. However,
in the same vain as Sherief's comment, this management of creation
doesn't need to be as exposed, and the bigger problem is that the client
shouldn't be required to directly manage heap allocated
objects. A slight modification of the API to make it a "Bridge" pattern,
would achieve the parallel C/C++ interface with the
encapsulation/opacity that Chris wants, and would not sacrifice object
creation/management by the client. This should make Sherief happy as
well :) Here's the modification (note my example isn't complete but
enough to illustrate the point):
1. Take the class that you are calling ALUTStream now and call it
AbstractStreamImpl, so that StreamImpl inherits from it. Here's the
header http://stuff.vaillant.fastmail.fm/ALUTStream/ALUTStreamImp.h
This header does *not* get distributed.
2. Create another class called ALUTStream. This is the "interface"
class. It's only member data is a pointer to a AbstractStreamImpl.
http://stuff.vaillant.fastmail.fm/ALUTStream/ALUTStream.h. This header *does* get
distributed together with the C header. The cpp is
http://stuff.vaillant.fastmail.fm/ALUTStream/ALUTStream.cpp
Note the constructors, which instantiate the appropriate instance of the
AbstractStreamImpl subclass.
3. Now just change the functions alutCreateStreamFromFile and
alutCreateStreameFromFileImage to create an instance
of ALUTStream instead of StreamImpl. Otherwise the "C style" interface
stays the same.
So now a C++ client can look like this:
ALUTStream stream("mySound.ogg", 100, 100);
stream.PlayOnSource(source);
stream.Poll();
// and can use the C interface if preferred.
alutStreamPlayOnSource(&stream, source);
alutStreamIsPlaying(&stream);
// And if you want to allocate on the heap instead
ALUTStream* pStream = new("mySound.ogg", 100, 100);
pstream->PlayOnSource(source);
pStream->Poll();
alutStreamIsPlaying(pStream);
alutStreamPlayOnSource(pStream, source);
Changing the implementation subclass is not a problem and doesn't change
client code, nor does it require a rebuild of the client.
Chris, if you go with your original API, I think that many good C++
developers would not use it directly and instead would would just wrap
the interface in an Adaptor class that essentially implements what I've
done here with the bridge.
Just an aside. A huge benefit of C++ is being able to encapsulate the
management of heap allocations so that 99% of the objects you manage
*directly* are created on the stack (even in the case of dynamic
binding) and are thereby automatically controlled by scope rules. You
get this for free without garbage collection. I learned to really
appreciate this about C++ when I started to work with ObjectiveC, where
every object must be heap allocated.
Marc
PS. I'm on irc.freenode.net #openal and #cw (nick is kb1ooo) if you're
itching to tell me that I'm totally wrong:)