1

The author of the PinChangeInt library and the EnableInterrupt library states at github:

When the PinChangeInt was in a .h and .cpp file, the Arduino IDE tried to compile it twice. I have a technique that you can use- #define LIBCALL_ENABLEINTERRUPT - that if you put it ahead of your #include in all files but the sketch, then you should be able to use the library without conflicts. If you know how to make it work in another way- remember, this is a library and will not compile in the same directory as the sketch and any other .h/.cpp/.ino files- then I'm all ears.

I came there, because both libraries failed to build with linker errors due to double definitions. So far so good. I split the PinChangeInt library in .h and .cpp myself and it compiled flawlessly. I did not try to split the other library yet because it looks like much work to me.

So now my question(s): Is GreyGnome right with his statement, that library code gets compiled twice when split in code and headers? And if so, what are the consequences? Memory waste? Runtime errors? What about the many other (working) libraries exhibiting separate code and header files?

Ariser
  • 577
  • 1
  • 8
  • 25

2 Answers2

1

Header files are parsed by the preprocessor and the compiler wherever they are included.

There's nothing weird about that. One can avoid multiple inclusions by using header guards. That's pretty standard too.

I have not looked at the library, but, for example, a typical error is to not declare variables as extern in the header and instead leave the real declaration in the header, which will lead to multiple instances, possibly clashing at link time. Variables that are part of the library API should be declared as extern in the header and the real declaration should be done only in the .C/.CPP file.

Interrupts are an even more special case: the AVR gcc relies on very specific names to identify interrupt handlers and each of them can be present at most once. So there are even more chances for having linker problems, if not declared correctly.

Igor Stoppa
  • 2,125
  • 1
  • 15
  • 20
0

To elaborate on your comment ~

An easy way to prevent multiple definitions from re-inclusion of the same header file is to ensure it's only defining things once.

e.g. my_header.h

#ifndef __MY_HEADER_H__
#define __MY_HEADER_H__

// some header code.

#endif //#ifndef __MY_HEADER_H__

When the header is first compiled, the definition of __MY_HEADER_H__ gets created by the compiler due to the 2nd line of the file. On subsequent compiles, which are caused by including the same header in more files, the 1st line '#ifndef' conditional skips everything down to the '#endif'.

When you write (or split) C / C++ code, it's common to wrap the class and function definitions (which go in the .h header file) in these compilation control blocks, and then the .c/.cpp file only gets the function bodies.

e.g.: my_rgb.h ~

#ifndef __MY_RGB_H__
#define __MY_RGB_H__

#include <stdint.h>

uint32_t packRGB(uint8_t r, uint8_t g, uint8_t b);

class RGB
{
public:
    RGB(uint8_t r, uint8_t g, uint8_t b);

    void toGreyscale();

private:
    uint8_t red;
    uint8_t green;
    uint8_t blue;
};

#endif //#ifndef __MY_RGB_H__

and my_rgb.cpp

#include "my_rgb.h"

// Note: this would typically be a member function
// but is separate to show a standalone function definition.
uint32_t packRGB(uint8_t r, uint8_t g, uint8_t b)
{
    uint32_t packed;

    packed = (r << 24) | (r << 16) | b;

    return packed;
}

RGB:RGB(uint8_t r, uint8_t g, uint8_t b)
{
    red = r;
    green = g;
    blue = b;
}

void RGB::toGreyscale()
{
    uint8_t grey = 0.2126 * (float)r + 0.7152 * (float)g + 0.0722 * (float)b;
    r = g = b = grey;
}

Including the header file multiple times will give "redefinition" errors at compile time. Including the same function multiple times will give double definitions at link time.

If you stick to this method of laying out .h and .c/.cpp files you will never have a problem with multiple definitions on your own files. (Unless you re-define a library function by accident, etc.)

Of course it's ok (and quite common) to define short little functions in the header/.h file too. (The compiler typically generates these as inline code though.) I'm not trying to explain the complete workings of headers & .C files, just presenting you a method which will always prevent multiple definition problems.

Kingsley
  • 773
  • 5
  • 12