You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
		
		
		
		
		
			
		
			
				
					
					
						
							185 lines
						
					
					
						
							6.9 KiB
						
					
					
				
			
		
		
	
	
							185 lines
						
					
					
						
							6.9 KiB
						
					
					
				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 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. :(
 | 
						|
 |