2

Let's say you are writing a library and you have a bunch of utility functions you have written just for yourself. Of course, you wouldn't want these functions to have external linkage so that they won't get mixed up by your library users (mostly because you are not going to tell the outside world of their existence)

On the other hand, these functions may be used in different translation units, so you want them to be shared internally.

Let's give an example. You have a library that does some stuff and in different source files you may need to copy_file and create_directory, so you would implement them as utility functions.

To make sure the user of your library doesn't accidentally get a linkage error because of having a function with the same name, I can think of the following solutions:

  • Terrible way: Copy paste the functions to every file that uses them adding static to their declaration.
  • Not a good way: Write them as macros. I like macros, but this is just not right here.
  • Give them such a weird name, that the chances of the user producing the same name would be small enough. This might work, but it makes the code using them very ugly.
  • What I do currently: Write them as static functions in an internal utils.h file and include that file in the source files.

Now the last option works almost fine, except it has one issue: If you don't use one of the functions, at the very least you get a warning about it (that says function declared static but never used). Call me crazy, but I keep my code warning free.

What I resorted to do was something like this:

utils.h:

...
#ifdef USE_COPY_FILE
static int copy_file(/* args */)
{...}
#endif

#ifdef USE_CREATE_DIR
static int create_dir(/* args */)
{...}
#endif
...

file1.c:

#define USE_COPY_FILE
#define USE_CREATE_DIR
#include "utils.h"

/* use both functions */

file2.c

#define USE_COPY_FILE
#include "utils.h

/* use only copy_file */

The problem with this method however is that it starts to get ugly as more utilities are introduced. Imagine if you have 10 of such functions, you need to have 7~8 lines of define before the include, if you need 7~8 of these functions!

Of course, another way would be to use DONT_USE_* type of macros that exclude functions, but then again you need a lot of defines for a file that uses few of these utility functions.

Either way, it doesn't look elegant.

My question is, how can you have functions that are internal to your own library, used by multiple translation units, and avoid external linkage?

Shahbaz
  • 46,337
  • 19
  • 116
  • 182
  • With the GNU toolchain at least, you can mark symbols as "local". – Oliver Charlesworth May 24 '12 at 09:32
  • @OliCharlesworth I do use `gcc`. Care to explain how to do that? (Preferably in an answer) – Shahbaz May 24 '12 at 09:33
  • I was about to, but then spotted it's a duplicate: [Restricting symbols in a Linux static library](http://stackoverflow.com/questions/393980/restricting-symbols-in-a-linux-static-library) – Oliver Charlesworth May 24 '12 at 09:34
  • I think the warning will probably go away if you mark the function `static inline` instead of just `static`. `inline` does have a minor subsidiary effect, it hints to the compiler that it should inline calls to the function, but GCC pretty much ignores that so it shouldn't cause any trouble. The coincidence that the `inline` keyword is spelled the same as its minor insignificant effect, instead of its major effect, is a historical accident ;-) – Steve Jessop May 24 '12 at 09:46
  • @SteveJessop, I had a suspicion that `inline` could come into play somehow, but I wasn't sure of the semantics of `inline` when mixed with `static`. Can you post that as an answer (also, explaining the historical accident? ;)) – Shahbaz May 24 '12 at 09:50
  • @OliCharlesworth, that question is in fact a duplicate (I wonder how I couldn't find it), but the answers are not convincing. One says use shared library, the other is `objcopy` which is good to know (really), but in the end of the day, it's not standard. What steve said is probably what I'd do. – Shahbaz May 24 '12 at 09:52
  • Also, "Give them such a weird name" - presumably you already have a plan to make it unlikely that the external symbols in your library clash with names in the users' code. You call them `shahbazlib_whatever` instead of just `whatever`. So you could just forget about hiding the symbols, define your internal functions as `shahbazlib_impl_copy_file`, don't put declarations for them in your library's public header file, and either don't document them, or document that `shahbazlib_impl` functions have undefined behavior when referred to or called by users. – Steve Jessop May 24 '12 at 09:53
  • @SteveJessop, I do that, but ... well ... uhm .... – Shahbaz May 24 '12 at 09:55
  • It's a bit wordy when you come to call them in your library code, I admit. – Steve Jessop May 24 '12 at 09:56
  • @Shahbaz: In what way is it not standard? Also, the problem with putting the functions in the header file as `static inline` is that you may end up with many duplicates in your final binary (although maybe the linker is clever and deals with that). And it offers no way to hide the "internals" of your utility functions (e.g. if they in turn rely on helper functions). – Oliver Charlesworth May 24 '12 at 09:56
  • @Shahbaz: Ah, you mean "standard" as in "standard C". Well, no, the C standard says nothing of object files and linking and so on. You will always be looking at a toolchain-dependent solution here. – Oliver Charlesworth May 24 '12 at 10:04
  • @OliCharlesworth, yes by standard I meant a standard C solution, portable to any compiler (I don't call visual C a compiler, so that can be excluded). Also, what do you mean by "And it offers no way to hide the "internals" of your utility functions (e.g. if they in turn rely on helper functions)"? – Shahbaz May 24 '12 at 10:07
  • @Shahbaz: Well, say `create_dir` calls a whole bunch of its own helper functions `create_dir_foo`, `create_dir_bar`, etc. From an encapsulation point-of-view, it would be nice if you didn't have to expose those to the rest of your library code. If everything's in header files, you cannot hide them. – Oliver Charlesworth May 24 '12 at 10:09
  • @OliCharlesworth, that may be true, but I am willing to sacrifice that. After all, I (or any other collaborators) would know about those extra functions and if I make a mistake and name another function with the same name, I can immediately fix that. – Shahbaz May 24 '12 at 10:20
  • The obvious way seems to wrap all the function pointers inside a struct. That will reduce the number of public names to one per "module". See `struct file_operations {}` The problem is that the struct pointer still has to be passed to functions, or embedded in other data structures as context, but the functions themselves are not exposed. – wildplasser May 24 '12 at 10:26
  • @wildplasser, that's interesting. I'll give it some thought – Shahbaz May 24 '12 at 10:33
  • That is how the linux kernel handles mountable file systems (and more). What remains is the shared layout of the struct ("format literal") which becomes part of the API. Plus at least one public visible name, of course. – wildplasser May 24 '12 at 11:08
  • @wildplasser It doesn't have to become part of the API, because you can pass it around as a `void *` casting it to its correct type (which is defined in an internal-never-shared header) in the library functions themselves. I'd upvote it if you post it as an answer, by the way. – Shahbaz May 24 '12 at 11:52
  • I beg to differ. The struct *layout* still has to be shared by all those using it. That's what gives the void* (or whatever pointer) its meaning. And I don't care too much about karma. – wildplasser May 24 '12 at 11:59
  • @wildplasser, the struct layout _is_ shared. Except it's a `struct` in a header included only in the library files and not available to the users. So the struct's name is not available to the users and therefore does not become part of the API. – Shahbaz May 24 '12 at 12:04

2 Answers2

3

Marking the functions static inline instead of static will make the warnings go away. It will do nothing about the code bloat of your current solution -- you're putting at least one copy of the function into each TU that uses it, and this will still be the case. Oli says in a comment that the linker might be smart enough to merge them. I'm not saying it isn't, but don't count on it :-)

It might even make the bloat worse, by encouraging the compiler to actually inline calls to the functions so that you get multiple copies per TU. But it's unlikely, GCC mostly ignores that aspect of the inline keyword. It inlines calls or not according to its own rules.

That's basically the best you can do portably. There's no way in standard C to define a symbol that's external from the POV of certain TUs (yours), but not from the POV of others (your users'). Standard C doesn't really care what libraries are, or the fact that TUs might be linked in several steps, or the difference between static and dynamic linking. So if you want the functions to be actually shared between your TUs, without any external symbol that could interfere with users of the library, then you need to do something specific to GCC and/or your static library or dll format to remove the symbols once the library is built but before the user links against it.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • I'm looking in the C99 standard, and I see their suggestion for warnings in Annex I: _An object is defined but not used_, but I can't find anywhere that would say `static inline` should _not_ generate a warning. The way I see it, an unused `static inline` function is still "a defined function that is not used". Is your answer based solely on gcc, or is it a well-defined behavior? – Shahbaz May 24 '12 at 10:31
  • @Shahbaz: except for required diagnostics, warnings aren't well-defined. That includes the ones recommended in the standard but not required. – Steve Jessop May 24 '12 at 10:36
  • Inline functions are commonly defined in header files. It makes no sense to assume they'd be used whenever the file is included. Compilers won't warn about a known good practice. But for `gcc`, you can use `attribute((unused))`. This means the function is meant to be possibly unused, and suppresses the warning. – ugoren May 24 '12 at 11:30
2

You can link your library normally, having these functions global, and localize them later.

objcopy can take global symbols and make them local, so they can't be linked with. It can also delete the symbol (the function stays, resolved references to it remain resolved, just the name is gone).

objcopy -L symbol localizes symbol. You can repeat -L multiple times.
objcopy -G symbol keeps symbol global, but localizes all others. You can repeat it also, and it will keep global all those you specified.

And I just found that I'm repeating the answer to this question, which Oli Charlesworth referenced in his comment.

Community
  • 1
  • 1
ugoren
  • 16,023
  • 3
  • 35
  • 65