Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
7.0 KiB

  1. Lessons learned about how to make a header-file library
  2. V1.0
  3. September 2013 Sean Barrett
  4. Things to do in an stb-style header-file library,
  5. and rationales:
  6. 1. #define LIBRARYNAME_IMPLEMENTATION
  7. Use a symbol like the above to control creating
  8. the implementation. (I used a far-less-clear name
  9. in my first header-file library; it became
  10. clear that was a mistake once I had multiple
  11. libraries.)
  12. Include a "header-file" section with header-file
  13. guards and declarations for all the functions,
  14. but only guard the implementation with LIBRARYNAME_IMPLEMENTATION,
  15. not the header-file guard. That way, if client's
  16. header file X includes your header file for
  17. declarations, they can still include header file X
  18. in the source file that creates the implementation;
  19. if you guard the implementation too, then the first
  20. include (before the #define) creates the declarations,
  21. and the second one (after the #define) does nothing.
  22. 2. AVOID DEPENDENCIES
  23. Don't rely on anything other than the C standard libraries.
  24. (If you're creating a library specifically to leverage/wrap
  25. some other library, then obviously you can rely on that
  26. library. But if that library is public domain, you might
  27. be better off directly embedding the source, to reduce
  28. dependencies for your clients. But of course now you have
  29. to update whenever that library updates.)
  30. If you use stdlib, consider wrapping all stdlib calls in
  31. macros, and then conditionally define those macros to the
  32. stdlib function, allowing the user to replace them.
  33. For functions with side effects, like memory allocations,
  34. consider letting the user pass in a context and pass
  35. that in to the macros. (The stdlib versions will ignore
  36. the parameter.) Otherwise, users may have to use global
  37. or thread-local variables to achieve the same effect.
  38. 3. AVOID MALLOC
  39. You can't always do this, but when you can, embedded developers
  40. will appreciate it. I almost never bother avoiding, as it's
  41. too much work (and in some cases is pretty infeasible;
  42. see http://nothings.org/gamedev/font_rendering_malloc.txt ).
  43. But it's definitely something one of the things I've gotten
  44. the most pushback on from potential users.
  45. 4. ALLOW STATIC IMPLEMENTATION
  46. Have a #define which makes function declarations and
  47. function definitions static. This makes the implementation
  48. private to the source file that creates it. This allows
  49. people to use your library multiple times in their project
  50. without collision. (This is only necessary if your library
  51. has configuration macros or global state, or if your
  52. library has multiple versions that are not backwards
  53. compatible. I've run into both of those cases.)
  54. 5. MAKE ACCESSIBLE FROM C
  55. Making your code accessible from C instead of C++ (i.e.
  56. either coding in C, or using extern "C") makes it more
  57. straightforward to be used in C and in other languages,
  58. which often only have support for C bindings, not C++.
  59. (One of the earliest results I found in googling for
  60. stb_image was a Haskell wrapper.) Otherwise, people
  61. have to wrap it in another set of function calls, and
  62. the whole point here is to make it convenient for people
  63. to use, isn't it? (See below.)
  64. I prefer to code entirely in C, so the source file that
  65. instantiates the implementation can be C itself, for
  66. those crazy people out there who are programming in C.
  67. But it's probably not a big hardship for a C programmer
  68. to create a single C++ source file to instantiate your
  69. library.
  70. 6. NAMESPACE PRIVATE FUNCTIONS
  71. Try to avoid having names in your source code that
  72. will cause conflicts with identical names in client
  73. code. You can do this either by namespacing in C++,
  74. or prefixing with your library name in C.
  75. In C, generally, I use the same prefix for API
  76. functions and private symbols, such as "stbtt_"
  77. for stb_truetype; but private functions (and
  78. static globals) use a second underscore as
  79. in "stbtt__" to further minimize the chance of
  80. additional collisions in the unlikely but not
  81. impossible event that users write wrapper
  82. functions that have names of the form "stbtt_".
  83. (Consider the user that has used "stbtt_foo"
  84. *successfully*, and then upgrades to a new
  85. version of your library which has a new private
  86. function named either "stbtt_foo" or "stbtt__foo".)
  87. Note that the double-underscore is reserved for
  88. use by the compiler, but (1) there is nothing
  89. reserved for "middleware", i.e. libraries
  90. desiring to avoid conflicts with user symbols
  91. have no other good options, and (2) in practice
  92. no compilers use double-underscore in the middle
  93. rather than the beginning/end. (Unfortunately,
  94. there is at least one videogame-console compiler that
  95. will warn about double-underscores by default.)
  96. 7. EASY-TO-COMPLY LICENSE
  97. I make my libraries public domain. You don't have to.
  98. But my goal in releasing stb-style libraries is to
  99. reduce friction for potential users as much as
  100. possible. That means:
  101. a. easy to build (what this file is mostly about)
  102. b. easy to invoke (which requires good API design)
  103. c. easy to deploy (which is about licensing)
  104. I choose to place all my libraries in the public
  105. domain, abjuring copyright, rather than license
  106. the libraries. This has some benefits and some
  107. drawbacks.
  108. Any license which is "viral" to modifications
  109. causes worries for lawyers, even if their programmers
  110. aren't modifying it.
  111. Any license which requires crediting in documentation
  112. adds friction which can add up. Valve used to have
  113. a page with a list of all of these on their web site,
  114. and it was insane, and obviously nobody ever looked
  115. at it so why would you care whether your credit appeared
  116. there?
  117. Permissive licenses like zlib and BSD license are
  118. perfectly reasonable, but they are very wordy and
  119. have only two benefits over public domain: legally-mandated
  120. attribution and liability-control. I do not believe these
  121. are worth the excessive verbosity and user-unfriendliness
  122. these licenses induce, especially in the single-file
  123. case where those licenses tend to be at the top of
  124. the file, the first thing you see. (To the specific
  125. points, I have had no trouble receiving attribution
  126. for my libraries; liability in the face of no explicit
  127. disclaimer of liability is an open question.)
  128. However, public domain has frictions of its own, because
  129. public domain declarations aren't necessary recognized
  130. in the USA and some other locations. For that reason,
  131. I recommend a declaration along these lines:
  132. // This software is dual-licensed to the public domain and under the following
  133. // license: you are granted a perpetual, irrevocable license to copy, modify,
  134. // publish, and distribute this file as you see fit.
  135. I typically place this declaration at the end of the initial
  136. comment block of the file and just say 'public domain'
  137. at the top.
  138. I have had people say they couldn't use one of my
  139. libraries because it was only "public domain" and didn't
  140. have the additional fallback clause, who asked if
  141. I could dual-license it under a traditional license.
  142. My answer: they can create a derivative work by
  143. modifying one character, and then license that however
  144. they like. (Indeed, *adding* the zlib or BSD license
  145. would be such a modification!) Unfortunately, their
  146. lawyers reportedly didn't like that answer. :(