When to implement functions in the header?

6

I often split the program into small modules, each with its own header and implementation. In general, the level of abstraction used allows the implementation to be completely changed without breaking the rest of the module-dependent code.

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

modulo.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

modulo.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 with this approach are that you have to recompile all the files that depend on the module in question should the implementation change, take longer to compile, and create functions that would not have previously existed - which were declared static and implemented inline by the compiler.

Are there any other disadvantages in this practice? When to implement the functions in the header?

    
asked by anonymous 30.01.2014 / 16:58

4 answers

6

When you include functions in headers you should mark them with inline , and you do not need to reimplement them in source. Just once is enough.

Advantages:

  • Speed: The compiler can perform much better optimizations of the code of each function when compiling the client code. This is especially true for small one- or two-line functions. For large functions or little used, 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 into a few seconds or minutes. If you've already used boost , you feel it on your skin.

  • Executable size: If your implementation is put into a shared library, it will weigh once only on disk and in memory. Imagine if libc were all implemented in headers. The entire 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 you change an implementation, all the code you use will need to be recompiled. More time spent, more work done.

  • Binary compatability: If the function put into a shared library is modified so that its declaration in the header does not change, you can change the .dll / .so / .dylib to the newer one in the program that uses it without need to recompile anything. It will be a transparent update.

Conclusion:

If it is a critical function, small and widely used, it may be worth it. Otherwise, it's worth thinking if it's really necessary to set it in the header.

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

    
30.01.2014 / 17:10
3

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

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

Inline note : Just use when it is a very simple code, that does not exceed 10 lines .. because in any case, the compiler will unmark your inline if you have exceeded .. as well as mark as inline functions which you forgot to put the keyword 'inline'.

In cases where you are sure to be inline, there is the __forceinline directive, in which case the compiler respects the programmer's wishes. One note 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 you know how and when to use it):

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 the inline the function code is exposed "pure" in the binary executable:

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

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

Can you make the code more efficient? It may, but as long as it is used correctly, otherwise it can slow down the program with bugs if used exaggeratedly. The best is not even to use force inline and let the compiler make the decisions on its own .

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

    
30.01.2014 / 18:38
0

At first I noticed an error ...

MODULO.H

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

MODULO.C

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

Ideally, if the module has not been defined, then define it. This should be inside modulo.h and not modulo.c. In modulo.c, simply "#include modulo.h"

The idea of headers is (as you've already mentioned) not having to recompile everything you've done previously. Imagine that in the main project (Main.c) you use a class that is already working perfectly, and you are only making changes in Main.c, in case you recompile, the class will be compiled again and there is no need for this. Thinking about a small project certainly will 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 each minimal change made to Main.c recompiled the entire project (network headers, visual headers, file management), it would take a long time.

Another great advantage is the code organization. For you not make the mistake of changing what already worked perfectly and also know where to find certain part of the project.

    
30.01.2014 / 17:06
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 includes this header, and the compiler will also understand that this function is a function different 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 up multiple code redundancies, and will also increase the compile time. This is why normally the implementation of these functions is done in .cpp and the headers only carry the declaration.

In addition, you can observe that when compiling the program, each .cpp generates a .obj distinct that will be then processed by the linker to generate the final executable binary. When you recompile after making a change to the program, only the affected files are recompiled, to speed up. That means you do not want to change headers all the time, because all the .cpp who are using them will need to be recompiled as well. Avoiding tinkering with headers dribbles this problem and saves time.

    
30.01.2014 / 19:18