How to declare an inline function in C99 multi-file project?

16,258

Solution 1

Unfortunately not all compilers are completely complying to C99 in that point even if they claim that they'd be.

An conforming way to do this is

// header file. an inline definition alone is
// not supposed to generate an external symbol
inline void toto(void) {
  // do something
}

// in one .c file, force the creation of an
// external symbol
extern inline void toto(void);

Newer versions of gcc, e.g, will work fine with that.

You may get away with it for other compilers (pretenders) by defining something like

#ifdef PRETENDER
# define inlDec static
# define inlIns static
#else
# define inlDec 
# define inlIns extern
#endif
// header file. an inline declaration alone is
// not supposed to generate an external symbol
inlDec inline void toto(void) {
  // do something
}

// in one .c file, force the creation of an
// external symbol
inlIns inline void toto(void);

Edit:

compilers with C99 support (usually option -std=c99) that I know of

  • gcc (versions >= 4.3 IIRC) implements the correct inline model
  • pcc is also correct
  • ggc < 4.3 needs a special option to implement the correct model, otherwise they use their own model that results in multiple defined symbols if you are not careful
  • icc just emits symbols in every unit if you don't take special care. But these symbols are "weak" symbols, so they don't generate a conflict. They just blow up your code.
  • opencc, AFAIR, follows the old gcc specific model
  • clang doesn't emit symbols for inline functions at all, unless you have an extern declaration and you use the function pointer in one compilation unit.
  • tcc just ignores the inline keyword

Solution 2

If used by itself, in C99 inline requires that the function be defined in the same translation unit as it's being used (so, if you use it in lib1.c, it must be defined in lib1.c).

You can also declare a method as static inline (and put the definition in a header file shared between two source files). This avoids the multiple-definition issue, and lets the compiler inline the file across all the translation units where it's used (which it may or may not be able to do if you just declare the function in one translation unit).

See: http://www.greenend.org.uk/rjk/2003/03/inline.html

Share:
16,258

Related videos on Youtube

mousomer
Author by

mousomer

Things I do: C++, Python, SVM, AI, ML Video compression, Wavelets, General Math, Games theory, Choice theory, Statistical reasoning, Poetry, Cats(x2), Parenting(x2).

Updated on April 23, 2020

Comments

  • mousomer
    mousomer about 4 years

    I want to define an inline function in a project, compiled with c99. How can I do it? When I declare the function in a header file and give the detail in a .c file, the definition isn't recognized by other files. When I put the explicit function in a header file, I have a problem because all .o files who use it have a copy of the definition, so the linker gives me a "multiple definition" error.

    What I am trying to do is something like:

    header.h
    inline void func()
    {
        do things...
    }
    
    
    lib1.c
    #include "header.h"
    ...
    
    lib2.c
    #include "header.h"
    

    with a utility which uses both lib1.o and lib2.o

  • Lindydancer
    Lindydancer over 13 years
    The term "translation unit" include the source file (lib1.c) and all header files that are included. This means that it should be OK to place it in header.h. Either the compiler is broken, or the function is declared as extern, which would account for this.
  • Lindydancer
    Lindydancer over 13 years
    The multiple definitions could originate from having an extern declaration of the function in a header file. This would force every translation unit to emit a copy of the function.
  • Jens Gustedt
    Jens Gustedt over 13 years
    @Lindydancer: sure. But what I say is that some compilers produce the symbol even if you only have inline declarations. I'll add some more remarks on specific compilers.
  • mousomer
    mousomer over 13 years
    The funny thing is - the header was guarded by #ifndef. But I think it did.t work because the compiler still put the header in each .o file.
  • mousomer
    mousomer over 13 years
    Thanks Jens. It worked (gcc 4.4.5 with flags std=gnu99 and std=c99).
  • mpontillo
    mpontillo over 11 years
    @JensGustedt, is there a benefit to doing this the "new way", as opposed to simply using static inline in the header file in all cases?
  • Jens Gustedt
    Jens Gustedt over 11 years
    @Mike, yes, in my POV there are two. First you don't blow up your executable with the copies of the function if the compiler decides not to inline. Then, the function is uniquely identifyable by its address, all pointers to it will compare equal, even if they originate in different compilation units. BTW, just declaring it static would do exactly the same as static inline, inline there is almost usesless. Modern compilers don't decide whether or not they inline on their own, programmers are notoriously bad in optimizing that.
  • mpontillo
    mpontillo over 11 years
    @Jens, thanks; I was thinking of the case where you'd use this alongside an always_inline attribute. In that case it seems like there is no benefit.
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    Are you sure this answer is completely correct? If I am understanding the standard correctly, using the non-pretender approach could cause additional problems, such as that if the toto(void) function is also defined in a different translation unit, which of the two will be executed is undefined. See 6.7.4-7 and 6.7.4-8 here: open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf I might be wrong, however.
  • Jens Gustedt
    Jens Gustedt over 11 years
    @rsanchezsaez, I am not sure if I understand you correctly. But if there are two different definitions of a function in the same compilation unit the behavior is undefined in any case. So no compilation unit that includes the header with the inline definition can contain an additional definition. I read the text of the standard that it is undefined if the visible definition is used (either by creating a "secret" local copy or by "inlining" the function) of if just a call to the function symbol is emitted.
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    Ok, I think you are right and I misread the standard. I don't understand the problem that the example from the sections I pointed out is trying to show. I think my problem lies in that I don't fully understand the difference between declaration and definition. According to this: stackoverflow.com/questions/1410563/… extern double fahr(double); should be a declaration. But the standard seems to say that this line is a definition instead. In your example, you seem to use definitions in the header and declarations in the .c.
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    Also, an additional question comes to mind: would it not be clearer to write the function implementation in the .c as extern inline void toto(void) { // do stuff } rather than in the .h? Is there any difference in writing the implementation there? Both seem to compile file.
  • Jens Gustedt
    Jens Gustedt over 11 years
    @rsanchezsaez, if you write the implementation in the .c inline makes no sense anymore. The important thing with inline is that all translation units see the definition (and it is the same for everyone) and the compiler may decide whether or not put the code in place or to call the function trough the external symbol.
  • Jens Gustedt
    Jens Gustedt over 11 years
    @rsanchezsaez, for the declaration versus definition, I don't think that the standard says extern double fahr(double); is a definition, but it says that it transforms the internal definition (given with the inline) into an external definition. Standardeeze is a dialect of English that is not always easy to capture :)
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    Thanks for the clarifications. Good point about 'standardeeze', heh. Now I start to understand these issues. ;-)
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    To wrap this interesting discussion up, I will leave here this informative explanation which coincides nicely with your answer and provides further clarification for differences between the C99 and Gnu-C implementations of inline. Jens, thanks for sharing your insights.
  • Ricardo Sanchez-Saez
    Ricardo Sanchez-Saez over 11 years
    @Jens: I'm back again. I discovered that some linkers can inline functions from separate object files. If this is the case, then maybe my suggestion of implementing inline functions in the .c could "work" properly. Anyway, I agree that the safest and most compatible way is the one you propose.
  • chux - Reinstate Monica
    chux - Reinstate Monica about 4 years
    // header file. an inline declaration alone is has a function defintion after it. As a definition is also a declaration, does the comment equally apply. Should it be // header file. an inline declaration/definition alone is?
  • Jens Gustedt
    Jens Gustedt about 4 years
    @chux You made me edit a 9 year old answer, amazing!
  • chux - Reinstate Monica
    chux - Reinstate Monica about 4 years
    LSNED * 9 * 365 ---> we should all be like Marvin by now.
  • rosshjb
    rosshjb over 3 years
    Your answer helped me. But i think it isn't correct to say "generates/creates an external symbol" for defining external definition(having object code to use for other/itself translation units). A symbol entry is always created in symbol table even if it is not defined at all(The resolution for it would be handled by linker). So, when you have a translation unit containing inline only function definition, there is the symbol for it in its object's symbol table. Of course, the symbol is not "defined", but there is.
  • rosshjb
    rosshjb over 3 years
    I assumed specifically that the inline function call will be compiled to real function call, not inlining in the comment above. When it is compiled to function inlining really, there is no symbol created, because function inlining happened and the function was gone.