When to implement header functions?

Asked

Viewed 1,099 times

7

I usually divide the program into small modules, each with its own header and implementation. In general, the abstraction level used allows the implementation to be completely changed without breaking the rest of the code that depends on the module.

In some benchmarks I ran using Callgrind, implementing the functions of a module widely used also in the header produced considerable gains in performance. In these cases I did the following:

module. h

#include <stdlib.h> //cabecalhos necessários

#ifndef MODULO_IMPLEMENTACAO
inline int funcao1(int x, int y)
{
    //código
}

inline int funcao2(int x, int y, double z)
{
    //código
}
#endif

module. c

#define MODULO_IMPLEMENTACAO
#include "modulo.h"

extern inline int funcao1(int x, int y)
{
    //código
}

extern inline int funcao2(int x, int y, double z)
{
    //código
}

If I understand correctly, the problems of this approach are the fact that I have to recompile all the files that depend on the module in question if the implementation changes, the fact that it takes longer to compile, and the creation of functions that before would not exist - the ones that were declared static and inline implementads by compiler.

Is there any other disadvantage in this practice? When implementing the functions in the header?

4 answers

7


When including functions in headers you should mark them with the inline, and there is no need to reimplement them at source. Just once is enough.

Perks:

  • Speed: The compiler will be able to perform much better optimizations of the code ownership of each function when compiling the client code. This is especially true for small one- or two-line functions. For large or poorly used functions, this difference is not significant.

Disadvantages:

  • Compile time: The compiler will have more code to parse when compiling each file. In a large project this can translate in a few seconds or minutes. If you have used the boost, felt it on the skin.

  • Executable size: If your implementation is put into a shared library, it will weigh on disk and memory only once. Imagine if the libc be implemented all in headers. Any executable would have a copy of the functions and when it was opened, it would store in memory code that could have been shared among other processes.

  • Recompile: When changing an implementation, all the code that uses it will need to be recompiled. More time spent, more work done.

  • Binary compactibility: If the function placed in a shared library is modified so that your statement in the header does not change, you can change the . dll/. so/. dylib for the newest in the program to use without needing to recompile anything. It will be a transparent update.

Completion:

If it’s a critical function, small and widely used, it might be worth it. Otherwise, it is worth wondering if it is really necessary to define in the header.

In the case of C++, this is in most cases inevitable with functions and templates classes. These need to be set in headers so that they are specialized for other types at each use.

  • I would include just the header in main.c. modulo.c is a file containing functions in case the compiler decides not to implement inline, http://stackoverflow.com/a/216546/2264920 (C99)

  • 1

    @Douglas, it’s not necessary. The compiler will always include header definitions in all files that include it (duplicates and unused ones are removed by Linker later). In your case you don’t even need to have the modulo.c.

  • but what about the definition provided on that link, is it incorrect or not understood correctly? What if the compiler decides to call an external function instead of implementing inline? " inline": no externally Visible Function is Emitted, but one Might be called and so must exist.

  • Another point that can be raised is that they only need to be declared in files . h functions that you want to reuse in other modules. If the function is "private" from that module it can stay only in the file . c/. cpp and be declared as inline if appropriate.

  • 1

    The true function of inline is to suppress duplicate function definition errors in Linker step and thus allow writing it in headers. The compiler is completely free to decide whether or not to apply optimization to any function you find.

3

When implementing inline in a header, look to place code directly in the header, without implementing to the source:

inline int soma(int a, int b) {
 return a + b;
}
// Sem inline só declaração, conforme padrão
void DoXYZ();

Note on the inline: Only use it when it’s a very simple code that doesn’t go beyond 10 lines.. because anyway, the compiler will uncheck your inline if you have exceeded it.. just as mark as inline functions which you forgot to put the keyword 'inline'.

In cases where you are sure that it should be inline, there is the directive __forceinline, and in that case the compiler respects the will of the programmer. One remark about force inline, is that each compiler can specify it in a different way, so it’s good to use a macro to replace it for each specific compiler.

What the inline directive should do (helps to know how and when to use):

When declaring a function in C or C++, the compiler will transform into the following pseudo-Assembly:

PUSH VAR1; // jogar valores do registro pra pilha
PUSH VAR2;
CALL SOMA; // executa função com os valores na pilha
{ pula para o código de máquina da FN1 .. }
POP VAR2; // retira os valores da pilha de volta aos registradores
POP VAR1; 

With inline the function code is exposed "pure" in binary executable:

JUMP SOMA
SOMA: 
 MOV {..}
 ADD {..}

The efficiency gained in this case is the economy of stack/stack push and pop operations and CALL instruction that is more expensive than a JUMP instruction

Can you make the code more efficient? It can, but since used in the correct way, otherwise it can make the program slower and buggy if used with exaggeration.. It’s even better not to use force inline and let the compiler make decisions automatically by itself.

Current compilers are extremely sophisticated. So use carefully and without exaggeration.

  • If the inline function receives there will be no JUMP, the code will be directly embedded in the Caller. In GCC/Clang, the inline force attribute is __attribute__((always_inline)).

  • You are right, William. The resulting binary will simply be copied as many times as is used directly in the code set in which it is being used. It continues reuse in source C, but in the result, it is always recopied for the times when the inline method is used. Save that JUMP instruction too. Thanks for the correction.

0

At first I noticed a mistake...

MODULE. H

#include <stdlib.h>
#ifndef MODULO_IMPLEMENTACAO
#define MODULO_IMPLEMENTACAO
... (restante)

MODULE. C

#include "modulo.h"
... (restante)

The ideal is that if the module has not been defined, it then defines. This should be within the module. h and not module.c. In module. c is enough the "#include module. h"

The idea of headlines is (as you have already mentioned) not having to recompile everything you had previously done. Imagine that in the main project (Main.c) you use a class that is already working perfectly, and you are only making changes to Main. c, if you recompile, the class will be compiled again and there is no need for this. Thinking about a small project will certainly not take long, but imagine something more extensive? How about we appeal to Linux that was programmed in C++? Imagine how long it would take if every minor change made in Main. c recompile the entire project (The network headers, the visuals, the file management), finally, it would take a long time.

Another great advantage is the organization of code. So you don’t make the mistake of changing what already worked perfectly and also know where to find a certain part of the project.

0

One of the problems of doing this is that the compiler will recompile this function several times, one for each .c or .cpp that include this header, and the compiler will also understand that this function is a separate function from each .c or .cpp despite having the same name and doing the same thing for all of them.

This will increase the size of the binary as it will bring several code redundancies, and will also increase the build time. This is why usually the implementation of these functions is done in a .cpp and headers bring only statement.

Also, you can Observer that when compiling the program, each .cpp generates a .obj which will then be processed by Linker to generate the final binary executable. When you recompile after making a change to the program, only the affected files are recompiled, to speed up. That means you don’t want to alter headers all the time because all the .cpp that are using them will need to be recompiled as well. Avoiding touching the headers you dribble this problem and gain time.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.