parent
							
								
									38c36fa048
								
							
						
					
					
						commit
						d4f114adcc
					
				
				 1 changed files with 184 additions and 0 deletions
			
			
		@ -0,0 +1,184 @@ | 
				
			||||
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 don't guard the implementation. 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 in the public domain. Where that dedication is not | 
				
			||||
// recognized, you are granted a perpetual, irrevocable license to copy | 
				
			||||
// and modify 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. :( | 
				
			||||
					Loading…
					
					
				
		Reference in New Issue