Lessons learned about how to make a header-file library V1.0 September 2013 Sean Barrett Things to do in an stb-style header-file library, and rationales: 1. #define LIBRARYNAME_IMPLEMENTATION Use a symbol like the above to control creating the implementation. (I used a far-less-clear name in my first header-file library; it became clear that was a mistake once I had multiple libraries.) Include a "header-file" section with header-file guards and declarations for all the functions, but only guard the implementation with LIBRARYNAME_IMPLEMENTATION, not the header-file guard. That way, if client's header file X includes your header file for declarations, they can still include header file X in the source file that creates the implementation; if you guard the implementation too, then the first include (before the #define) creates the declarations, and the second one (after the #define) does nothing. 2. AVOID DEPENDENCIES Don't rely on anything other than the C standard libraries. (If you're creating a library specifically to leverage/wrap some other library, then obviously you can rely on that library. But if that library is public domain, you might be better off directly embedding the source, to reduce dependencies for your clients. But of course now you have to update whenever that library updates.) If you use stdlib, consider wrapping all stdlib calls in macros, and then conditionally define those macros to the stdlib function, allowing the user to replace them. For functions with side effects, like memory allocations, consider letting the user pass in a context and pass that in to the macros. (The stdlib versions will ignore the parameter.) Otherwise, users may have to use global or thread-local variables to achieve the same effect. 3. AVOID MALLOC You can't always do this, but when you can, embedded developers will appreciate it. I almost never bother avoiding, as it's too much work (and in some cases is pretty infeasible; see http://nothings.org/gamedev/font_rendering_malloc.txt ). But it's definitely something one of the things I've gotten the most pushback on from potential users. 4. ALLOW STATIC IMPLEMENTATION Have a #define which makes function declarations and function definitions static. This makes the implementation private to the source file that creates it. This allows people to use your library multiple times in their project without collision. (This is only necessary if your library has configuration macros or global state, or if your library has multiple versions that are not backwards compatible. I've run into both of those cases.) 5. MAKE ACCESSIBLE FROM C Making your code accessible from C instead of C++ (i.e. either coding in C, or using extern "C") makes it more straightforward to be used in C and in other languages, which often only have support for C bindings, not C++. (One of the earliest results I found in googling for stb_image was a Haskell wrapper.) Otherwise, people have to wrap it in another set of function calls, and the whole point here is to make it convenient for people to use, isn't it? (See below.) I prefer to code entirely in C, so the source file that instantiates the implementation can be C itself, for those crazy people out there who are programming in C. But it's probably not a big hardship for a C programmer to create a single C++ source file to instantiate your library. 6. NAMESPACE PRIVATE FUNCTIONS Try to avoid having names in your source code that will cause conflicts with identical names in client code. You can do this either by namespacing in C++, or prefixing with your library name in C. In C, generally, I use the same prefix for API functions and private symbols, such as "stbtt_" for stb_truetype; but private functions (and static globals) use a second underscore as in "stbtt__" to further minimize the chance of additional collisions in the unlikely but not impossible event that users write wrapper functions that have names of the form "stbtt_". (Consider the user that has used "stbtt_foo" *successfully*, and then upgrades to a new version of your library which has a new private function named either "stbtt_foo" or "stbtt__foo".) Note that the double-underscore is reserved for use by the compiler, but (1) there is nothing reserved for "middleware", i.e. libraries desiring to avoid conflicts with user symbols have no other good options, and (2) in practice no compilers use double-underscore in the middle rather than the beginning/end. (Unfortunately, there is at least one videogame-console compiler that will warn about double-underscores by default.) 7. EASY-TO-COMPLY LICENSE I make my libraries public domain. You don't have to. But my goal in releasing stb-style libraries is to reduce friction for potential users as much as possible. That means: a. easy to build (what this file is mostly about) b. easy to invoke (which requires good API design) c. easy to deploy (which is about licensing) I choose to place all my libraries in the public domain, abjuring copyright, rather than license the libraries. This has some benefits and some drawbacks. Any license which is "viral" to modifications causes worries for lawyers, even if their programmers aren't modifying it. Any license which requires crediting in documentation adds friction which can add up. Valve used to have a page with a list of all of these on their web site, and it was insane, and obviously nobody ever looked at it so why would you care whether your credit appeared there? Permissive licenses like zlib and BSD license are perfectly reasonable, but they are very wordy and have only two benefits over public domain: legally-mandated attribution and liability-control. I do not believe these are worth the excessive verbosity and user-unfriendliness these licenses induce, especially in the single-file case where those licenses tend to be at the top of the file, the first thing you see. (To the specific points, I have had no trouble receiving attribution for my libraries; liability in the face of no explicit disclaimer of liability is an open question.) However, public domain has frictions of its own, because public domain declarations aren't necessary recognized in the USA and some other locations. For that reason, I recommend a declaration along these lines: // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. I typically place this declaration at the end of the initial comment block of the file and just say 'public domain' at the top. I have had people say they couldn't use one of my libraries because it was only "public domain" and didn't have the additional fallback clause, who asked if I could dual-license it under a traditional license. My answer: they can create a derivative work by modifying one character, and then license that however they like. (Indeed, *adding* the zlib or BSD license would be such a modification!) Unfortunately, their lawyers reportedly didn't like that answer. :(