Even then, often there will be small bits that don't fit well. By wrapping everything you make the model and assumptions of your application explicit. Code that is written against your own model (which is almost always simpler than what the library offers) will be more understandable.
I really like the Dijkstra quote, "The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise."
Usually I even wrap libc and POSIX functionality in my C programs. Advantages include (1) not having to mess with specific types, such as size_t, time_t, DIR*, and so on -- which often don't fit my own model (e.g. dealing with unsigned size_t vs signed integers is painful), and often need small fixes to be portable. (2) compiles a lot faster since I don't need to include 10000s of lines from system header files (3) not wrapping makes huge portions of the project dependent on the library, instead of only the wrapper. (4) You tightly control access to the library, which makes it easier to troubleshoot. You can also make up your own strategy for managing library objects in a central place.
I think this is nonsense and leads to enterprise-y type patterns and hard to understand code.
Wrapping things for the sake of it is a recipe for unnecessary verbosity and a hard to learn codebase as things are obfuscated and take longer to trace through.
In C, your point 1 makes me think your code is unsafe, if you're having trouble keeping track of types and signedness. Point 2 is fair enough, but I think a lot of C headers are granular enough that it's irrelevant, especially given how fast everything is these days. Gone are the 8 hour builds of old.
Point 3 is precisely the type of premature interface-isation that's the problem here. If you're never going to change it, then it doesn't matter whether huge portions of the code rely on the library or your wrapper. It's basically the same thing. Only you've put in extra work to wrap something in another layer for no gain. 4 much the same.
Hmm, I think I should put that in context. I'm not saying write a wrapper for the sake of it. I don't wrap everything in three layers of OO giftwrap. I'm saying one should follow basic rules of hygiene.
For example, I have a FreeType backend in my code, and I have a single file which implements a font interface (open font file, choose font size, draw to texture in the format of my app) by interfacing with Freetype. It returns my own error values, prints warnings with my own logging framework, puts the data in my own datastructures, etc. All things that Freetype could never do since it does not know my project.
I have an OpenGL backend, and I make sure that my geometry and math stuff, UI drawing code, and what not, does not depend on OpenGL, or OpenGL types. (Did that once when I learned OpenGL, and it was terrible). So now I have basically one file which is dependent on OpenGL, and it's there to simply put my internal datastructures on the screen, in a very straightforward way.
Same goes for my windowing backend. I use GLFW currently but I make sure I don't use e.g. GLFW_KEY_A in client code. I define my own enums and own abstractions. It does not come with any cost to simply have my own KEY_A, which means I can swap out the backend relatively easily, and can change my model more easily (for example, make up my own controls abstraction instead of relying on a standard PC 104 keys keyboard, etc).
> In C, your point 1 makes me think your code is unsafe, if you're having trouble keeping track of types and signedness.
No, it's safer precisely because I have a well-defined boundary where values get converted. I can check for overflows there, and not worry about the rest of my code.
There is no practical way to deal with size_t in every little place when normal values come as int.
> Point 2 is fair enough, but I think a lot of C headers are granular enough that it's irrelevant,
You bet. Have you ever tried to access parts of the Win32 API? You're immediately in for (I think it was) 30K lines of code even if you define WIN32_LEAN_AND_MEAN. Otherwise it's maybe more like 60K. It's crazy. Or check out GLEW (which I don't use it anymore). It generates 27000 lines of header boilerplate simply to access the OpenGL API.
Add to that that most headers have a standard #ifdef include guard, which means that while files get ignored the second time, the lexer has to parse the whole thing over and over again.
> If you're never going to change it
How would you know?
> then it doesn't matter whether huge portions of the code rely on the library or your wrapper.
It does matter a lot if there is a semantic mismatch.
You have a target system, and no current plans to change it. You can't predict every change that might happen down, and neither should you try - you'll waste a ton of engineering effort and if there is change, it's almost always change in ways you didn't anticipate.
I absolutely agree with your statement, but I don't think it applies in this situation. I'm not trying to predict future changes. It's the opposite. I'm codifying the current state. I make sure that there are no misconceptions about the extent to which the library is used.
Yes, a library is already an abstraction, just somebody else's. Thread parent is complaining about bad design that doesn't model the problem, not abstraction.
I really like the Dijkstra quote, "The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise."
Usually I even wrap libc and POSIX functionality in my C programs. Advantages include (1) not having to mess with specific types, such as size_t, time_t, DIR*, and so on -- which often don't fit my own model (e.g. dealing with unsigned size_t vs signed integers is painful), and often need small fixes to be portable. (2) compiles a lot faster since I don't need to include 10000s of lines from system header files (3) not wrapping makes huge portions of the project dependent on the library, instead of only the wrapper. (4) You tightly control access to the library, which makes it easier to troubleshoot. You can also make up your own strategy for managing library objects in a central place.