I'm posting this question to externalize my utmost recent dismay with programming, but it is still a question nevertheless.
Today I can't picture myself doing things without thinking about readability, orthogonality, encapsulation, abstractions, code intrusiveness, not repeating knowledge and not letting things blow during runtime when they can be foreseen during the compilation phase and gotten the programmers warned about them (that's one of the reasons I like strong typing; I've learned to take it as an advantage to warn me about problems sooner).
To me, this is good code, and that's what I seek to do. And I felt like I could push these values to extremes to make code I could be proud of for much longer (I generally start hating my code after a week without touching it).
... until I found myself having to deal with interdependent low-level C and pseudo-OOP API's such as WinAPI, DirectX and OpenGL.
I'd guess it's common sense that you should not disclose more information than you have to disclose to accomplish any given task. Generally this is achieved through well-designed abstraction, encapsulation and by respecting the Single Responsability Principle.
If you have a class "Crap" that exposes three distinct functionalities (disregards SRP) with all the guts exposed (disregards encapsulation), revealing more than what it does, but also how it does (disregards abstraction), and you have a function that needs one of these functionalities to work, you can't pass it a reference to such a class and expect it to really only use the one functionality it requires.
You can't fine-tune what can be done in which circumstances, since passing the full class instance exposes all three functionalities.
You can't guarantee integrity of state since clients can do whatever they want and, even if they want to keep object integrity, they'll incorporate a lot of contextual information regarding the class. When the class changes, you will have to remember to update its clients. Generally you won't remember everything, and your objects' states will start to intermittently appear inconsistent. Your code compiles (when you're out of luck), but your runtime is doomed.
You tie the classes' clients to the class implementation, and objects providing the same functionality these clients need will be useless to them.
The function writer could always think doing just that special something-else wouldn't hurt anyone. And the function client might expect the function not to ever do that. In a world full of expectations, many of them are to be broken, and when they are, they have to be tracked down -- and the hard way. In the context these expectations get broken, where no design principles were respected, they are bound to affect a plethora of unrelated parts of the system.
And while all the time I try to follow best practices when coding, I feel my crystal-clean design threatened by bad third-party libraries. And from what I saw, they are all really badly designed. All of them. Some of them follow some good practices, but they always think it doesn't hurt to punch some exceptions to these practices in their design and end up screwing all the rest. And they don't even notice it (I won't say what libraries come to my mind since I don't want to hurt anyone's feelings and I'm unsure as to whether I'm right or wrong there).
One could point out that if I'm so enraged by such libraries but am still stuck with them I should write wrapper libraries. Fantastic. I've set out to wrap WinAPI windowing and Direct3D rendering in two subsystems. But then I noticed I'd have to pass the raw HWND (window handle) from the windowing to the rendering subsystem and pray for it to only use this HWND in the way it's supposed to.
Remember the "Class Crap" example? Well, when I give away a HWND to a function, I should be expecting it to do ANYTHING IT WANTS with that handle, simply because it can. Direct3D should only have the right to render something on the window surface. Better still if I could give it a handle that even restricted in which portion(s) of the window it could render.
In practice it can even close the window. Toggle to fullscreen. Change the caption. Use the caption for something I didn't expect it to. You name it.
WGL (OpenGL) performs a little better and expects a display device context. Since it expects only that, I can trust it will only render stuff in a portion of a display device, right? Nope! You can always grab a handle to the window it's attached to if you want and do all the crap I mentioned before. True, it takes more guts for a function writer to do that, but it can still happen, and I feel I should be ready.
OGG/Vorbis decoder expects a non-const, null-terminated C-string to open a file. If I'm wrapping this API, should I tell my clients I'm taking a constant C-string as argument and cast away its constness to pass it to the underlying API, praying for it to keep my promise not to modify its contents?
Should I be polite and copy the buffer before sending it to the underlying API?
Should I take a non-const C-string too, and pull the responsability of this silly decision further away from the source of the problem, knowing that more and more const-incorrectness will spread into project code?
My question, then, is: how, just how can I free my code from the evil of these APIs? They totally scare the hell out of me. I don't wanna know how they are dealt with in real world, since I'm pretty sure these problems are most certainly ignored in real world projects.
There is a well-defined problem here: you have to wrap two interdependent APIs (one of them depends on the other; not necessarily the other way around) into two orthogonal APIs. The design must accomodate new orthogonal APIs which wrap more interdependent APIs in a safe manner. How to proceed?
To serve as an example, the problem which originated this question is available at http://stackoverflow.com/questions/4075931/how-to-best-encapsulate-window-handles.
The original question was posted at http://stackoverflow.com/questions/4103401/when-c-handles-pop-in-your-c-code-and-break-your-pretty-oo-design.