2

I am maintaining a FOSS library which, for the sake of discussion, consists of a small .h file and a larger .c file (plus build-related files, see below)

I've been requested to offer this library as a single header file. My instinct was to just refuse, as one could simply include both the .c and and the .h file, but I suppose maybe it's somehow useful for some people to enjoy this single-file convenience.

My question is: How should I arrange for both the two sources to be available, and the single-file version? There seem to be lots of options here:

  • Just duplicate the code, and have another .h with everything in it checked in to the repository.
  • Generate the combined .h as part of the build process - but, how do I do that effectively? Do I use regular expressions? I can't preprocess the files - that will result in all macros being expanded.
  • Switch to having a single .h in the repository, only, and generate the smaller .h dynamically - but then, same problem as the previous item - how does one do that effectively?
  • Decide it's a useless endeavor and refuse to offer a single header file.

What should I know about the feasibility, pros and cons of these options? And - is there other ones I've overlooked?

Notes:

  • The library's repository is on GitHub and there are occasional versioned releases of the source code.
  • I use CMake for build configuration and installation, so solutions requiring automation can rely on this fact. But since I made this a general question, describing other automation schemes is a legitimate answer.
3
  • 2
    Are you having a lot of requests for this? Or just one or two? Also, depending on how your library is written, just #including the .c file more than once might go badly at link time, so actually doing this might require more work than it's worth. If I were you I'd say 'sorry' and leave it unless you think it's an interesting task. Jan 6, 2022 at 11:34
  • @MichaelKohne: Just one. But - I've found that even serious (IMO) bugs often have just one, if any, notices filed - even for rather popular repositories. Still good points though: 1. Giving my .c file an include guard and 2. Raising the bar with something like "get 3 more people to ask for this and we'll talk".
    – einpoklum
    Jan 6, 2022 at 11:55
  • 1
    I suggest mentioning your library in your question, and explicitly give the github URL Jan 6, 2022 at 13:15

2 Answers 2

2

"Generate the combined .h as part of the build process - but, how do I do that effectively?"

If you are using classic include guards in your header file ...

 // foo.h
 #ifndef FOO_H
     #define FOO_H

 // ... here is the code

 #endif

and in your c file ...

 // foo.c
 #ifndef FOO_H
     #include "foo.h"
 #endif

 // just in case ...
 #ifndef FOO_C
     #define FOO_C
 // ... remaining code here
 #endif

a simple cat foo.h foo.c >combined_foo.h (Unix/Linux) or copy foo.h+foo.c combined_foo.h (Windows) should produce a working single-file version of your lib. The include guard in the .c will effectively prevent the inclusion in combined_foo.h for a file which is not distributed.

What remains is to call this cat command at the right place in your build process.

7
  • But doesn't C have something like C++'s ODR? Suppose the user includes this file in more than one of his files?
    – davidbak
    Jan 6, 2022 at 16:50
  • @davidbak: AFAIK no, see Does C have One Definition Rule like C++?. However, I guess this may be compiler (switch) dependent, hence one may have to add an additional include guard for the inner part of the .c file - I have edited my answer accordingly.
    – Doc Brown
    Jan 6, 2022 at 19:03
  • 1
    Actually, I think the problem would be that you'd have multiple definitions of the functions in different object files, and then the linker would (most likely) complain. Maybe you need to do something with inline or static inline as per this answer: stackoverflow.com/a/31108614/751579
    – davidbak
    Jan 6, 2022 at 19:22
  • @davidbak: I think the OP's source is suitable for being distributed in a single header file (or maybe their expectation is about the lib's user to make sure to include iit only in one compilation unit). Otherwise the whole question would not make sense.
    – Doc Brown
    Jan 6, 2022 at 19:31
  • IDK. In C++ the problem is kind of trivial: use the inline keyword on all functions (and with C++17, on variable decls) no problem. C is only slightly more work, but it looks like you need to know the special incantation, which that answer I linked gives.
    – davidbak
    Jan 6, 2022 at 19:35
0

A possible solution is to use GNU autoconf and related to be able to configure, at compile time, how is that library available and deployed.

Another solution is to use preprocessor tricks. When used with -DFOO_ALWAYS_INLINE your foo.h header file is defining static inline functions. Otherwise it is declaring them as extern.

Another possibility is to use some extra preprocessor (GNU m4, or GPP, or your own C code generator) which generates C code.

You could contact me by email.

1
  • 3
    The library uses CMake, I'll update the answer to mention that. But still, a legitimate answer.
    – einpoklum
    Jan 6, 2022 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.