Do you know what grinds my gears even more than undocumented libraries? Libraries with such complicated and contrived user interfaces that you need dozens of lines of code to simply initialize the library or perform any basic task with it, when it would be perfectly possible for the library to offer simpler convenience wrappers on top of the low-level details, but they don't.
Whenever I need to use such library, I always have to end up writing such a wrapper myself, which I then end up using in most projects which use that library.
Many amateur programmers write libraries for their own needs, and then they might publish the library for others to use. The problem with some of these amateurs is that they don't really have a good concept nor experience on usability. They don't invest time in designing the public interface of their libraries to be as simple and straightforward as possible, to make its usage as simple and easy as possible, while still supporting all needed features. They often end up exposing many things in the public interface that wouldn't need to be exposed, and moreover, end up making the usage of these things mandatory even though they could perfectly well have some default values or be called automatically by default (with default parameters).
Ok, that's understandable. Not everybody is a library design genius. What is much less understandable is when professional programmers and big programming companies who publish (or even standardize) libraries do this very same thing: They might expose all the nitty-gritty details of the internal workings of the library while not offering any convenience wrappers to make the whole thing easier to use.
For example, the OpenAL library is intended to be a counterpart of the OpenGL library, but for sounds. It's intended to be very portable, versatile and efficient. You wouldn't believe how much code needs to be written in order to play a sound (which is eg. loaded from a .wav or .mp3 file) with OpenAL. And that's just to play one sound. If you want to play the same sound many times repeatedly and simultaneously (ie. start playing the sound again before has ended, mixing the new one on top of the old one) you have to write even more code. Want to stream a sound from a large audio file instead of loading it fully in memory? You have to write even more code. And so on. The library offers next to no convenience functions or wrappers to make this whole process easier.
What I would want is that I could eg. write an array with the sound file names (and perhaps some options on how those sounds should be loaded and played), call a one-liner initialization function giving this array as parameter, and then when I want to play a sound, do it with a one-liner (this function might return some kind of handle to the sound being played eg. if I want to change its volume or pitch, or stop it midway, but the handle could well be completely ignored if I don't need to do anything with the sound). In other words, basically 2 lines of code to play a sound (besides the array which lists the audio file names). I don't care how the library is initialized, just use defaults. I don't care how the sound files are loaded and played, just do it as efficiently as possible (eg. make some educated guess based on the amount of sounds and their size). Perhaps offer the possibility of specifying if a sound should be preloaded into memory or streamed (either in the array or in the function which plays the sound), but that's all. Otherwise use defaults and do everything as efficiently as possible. I don't care about the nitty-gritty details. I don't want the nitty-gritty details. I don't want to have to write a couple dozen of lines of cryptic code to simply play a sound efficiently. Let me play sounds with one-liners.
If the nitty-gritty details are necessary for something (eg. because you are sending sounds to a very exotic hardware or something), offer them as an alternative. Don't make them mandatory.
And OpenAL is far from being the only example. Check out libpng. You wouldn't believe how many cryptic lines of code you have to write in order to read or write PNG files with it. I always end up having to write convenience wrappers around it to make its usage easier. (Much of this complexity and inconvenience comes from the fact that libpng has been designed to work even in 16-bit architectures where you can't load more than 64 kilobytes of data at a time. Ok, that's fine. The problem is that it offers absolutely no convenience wrappers to be used in architectures with no such limitations nor concerns.)
And let's not talk about libjpeg. You wouldn't believe how much code is needed to decode JPEG data which resides in memory (rather than a file). libpng is actually easy to use in comparison (even if the PNG data is in memory). (And decoding a JPEG from a file isn't much easier either.)
It's not like such libraries couldn't offer convenience wrappers, even if they are written in C. The zlib library is a good example of a C library which is surprisingly easy to use. If you know how to use the standard C file streaming functions then you know how to use zlib for the same purpose. zlib offers convenience functions which work almost identically to the standard C file reading/writing functions, and which hide all the complicated details behind them. That's how you design a library. (And this was in C. In C++ it would be even easier to write convenience wrappers, not to talk about even higher-level languages.)