1

I'm rewording this with actual files I was using (but greatly stripped. And I need to quit posting when I'm half-asleep.)

Test.hpp reads:

/*
 * test.hpp
 *
 *  Created on: Apr 1, 2019
 *      Author: Mike
 */

#ifndef INCLUDE_TEST_HPP_
#define INCLUDE_TEST_HPP_
#include <U8x8lib.h>



#define R1 13
#define RGROUND 12  //the rotary switch is connected via header pins on the board for development.
#define R2 14
#define SWITCH 27
#define SCL 15
#define SDA 4
#define OLED_RESET 16


#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#include <RotaryEncoder.h>
#include "OneButton.h"


U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
OneButton button(SWITCH, true);
RotaryEncoder encoder(R1, R2);

void testFunc();

#endif /* INCLUDE_TEST_HPP_ */
/*      end of test.hpp"   */

test.cpp reads as:

#include <Arduino.h>
#include "test.hpp"

#include <U8x8lib.h>
#include <RotaryEncoder.h>
#include "OneButton.h"

void setup() {
    u8x8.begin();
    u8x8.clearDisplay();
//  encoder.begin();    //there's no begin functions for either of these.
//  button.begin();
}

void loop() {
    encoder.tick();
    button.tick();
    u8x8.print("starting loop");
    testFunc();
}

test2.cpp reads

/*
 * test2.cpp
 *
 *  Created on: Apr 1, 2019
 *      Author: Mike
 */

#include "test.hpp";

void testFunc(){
    encoder.getPosition();
    //do some other stuff.
}

Compiling the above gives me the following error:

Linking .pioenvs\uno\firmware.elf
.pioenvs\uno\src\test2.cpp.o (symbol from plugin): In function `u8x8':
(.text+0x0): multiple definition of `u8x8'
.pioenvs\uno\src\test.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pioenvs\uno\src\test2.cpp.o (symbol from plugin): In function `u8x8':
(.text+0x0): multiple definition of `button'
.pioenvs\uno\src\test.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pioenvs\uno\src\test2.cpp.o (symbol from plugin): In function `u8x8':
(.text+0x0): multiple definition of `encoder'
.pioenvs\uno\src\test.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
*** [.pioenvs\uno\firmware.elf] Error 1

If I try adding the three lines with extern

extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
extern OneButton button(SWITCH, true);
extern RotaryEncoder encoder(R1, R2);

to the test.hpp and test2.cpp files and then to test.cpp without the extern, I get this error:

In file included from src\test.cpp:2:0:
include/test.hpp:34:46: warning: 'u8x8' initialized and declared 'extern'
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
include/test.hpp:35:24: warning: 'button' initialized and declared 'extern'
extern OneButton button(SWITCH, true);
^
include/test.hpp:36:29: warning: 'encoder' initialized and declared 'extern'
extern RotaryEncoder encoder(R1, R2);
^
src\test.cpp:8:39: error: redefinition of 'U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8'
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
In file included from src\test.cpp:2:0:
include/test.hpp:34:42: note: 'U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8' previously declared here
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
src\test.cpp:9:17: error: redefinition of 'OneButton button'
OneButton button(SWITCH, true);
^
In file included from src\test.cpp:2:0:
include/test.hpp:35:18: note: 'OneButton button' previously declared here
extern OneButton button(SWITCH, true);
^
src\test.cpp:10:22: error: redefinition of 'RotaryEncoder encoder'
RotaryEncoder encoder(R1, R2);
^
In file included from src\test.cpp:2:0:
include/test.hpp:36:22: note: 'RotaryEncoder encoder' previously declared here
extern RotaryEncoder encoder(R1, R2);
^
In file included from src\test2.cpp:8:0:
include/test.hpp:34:46: warning: 'u8x8' initialized and declared 'extern'
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
include/test.hpp:35:24: warning: 'button' initialized and declared 'extern'
extern OneButton button(SWITCH, true);
^
include/test.hpp:36:29: warning: 'encoder' initialized and declared 'extern'
extern RotaryEncoder encoder(R1, R2);
^
src\test2.cpp:10:46: warning: 'u8x8' initialized and declared 'extern'
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
src\test2.cpp:10:46: error: redefinition of 'U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8'
In file included from src\test2.cpp:8:0:
include/test.hpp:34:42: note: 'U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8' previously declared here
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);
^
src\test2.cpp:11:24: warning: 'button' initialized and declared 'extern'
extern OneButton button(SWITCH, true);
^
src\test2.cpp:11:24: error: redefinition of 'OneButton button'
In file included from src\test2.cpp:8:0:
include/test.hpp:35:18: note: 'OneButton button' previously declared here
extern OneButton button(SWITCH, true);
^
src\test2.cpp:12:29: warning: 'encoder' initialized and declared 'extern'
extern RotaryEncoder encoder(R1, R2);
^
src\test2.cpp:12:29: error: redefinition of 'RotaryEncoder encoder'
In file included from src\test2.cpp:8:0:
include/test.hpp:36:22: note: 'RotaryEncoder encoder' previously declared here
extern RotaryEncoder encoder(R1, R2);
^
*** [.pioenvs\uno\src\test.cpp.o] Error 1
*** [.pioenvs\uno\src\test2.cpp.o] Error 1
 [ERROR] Took 3.51 seconds 

Putting those three into test.hpp without the extern and then putting them into test.cpp and test2.cpp with extern gives me pretty much the same error.

  • You seem to be declaring `foo` as a function (you did say it is a C file, why is this tagged [tag:c++]?). You can't use the member access operator `.` on functions. – Ben Voigt Apr 01 '19 at 23:35
  • 2
    If you mean for `foo` to be an object of a C++ class type, you'll need `extern` in the header file, and exactly one definition without `extern` in one C++ compilation unit. If these are C++ source files, please fix your filenames. – Ben Voigt Apr 01 '19 at 23:38
  • There's a good question in here if you can clear it up. – user4581301 Apr 01 '19 at 23:39
  • I still don't know of `foo` is a function taking two parameters of types `spam` and `eggs`, returning a `Fubar`, or if `foo` is a variable of type `Fubar`, with two other values being passed to the constructor. Can you clarify which it is? – 1201ProgramAlarm Apr 02 '19 at 00:31

1 Answers1

2

You want one declaration with extern in the header. Just the declaration. So in Test.hpp

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);

becomes

extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8;

Obligatory Standard quote

When you stick extern on it and leave the initialization,

// bad code! Do not use!
extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8( ... );
                                             ^ initialization   

the extern is effectively ignored and you get a definition instead of a declaration. The compiler may warn you about this, and it did

include/test.hpp:34:46: warning: 'u8x8' initialized and declared 'extern'

But if you don't know what you're looking for, the message doesn't help much. Regardless, don't ignore warnings. They are the compiler telling you that while something compiles (it's syntactically correct) it probably doesn't mean what you want or do what you want (it's probably logically incorrect). You may find the warnings often contain more useful information than the actual error that results, so find out what they mean and eliminate them.

Getting back on topic,

extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8;

in test.hpp promises that u8x8 has been or will be defined somewhere else and is safe to use. The next step keeps the promise. In test.cpp XOR test2.cpp (one of the two, not both) add

U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/SCL, /* data=*/SDA, /* reset=*/OLED_RESET);

Do the same thing for button and encoder.

More reading

When to use extern in C++

and C, not C++, but C and C++ are close enough here for this longer discussion to be useful: How do I use extern to share variables between source files?

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • 1
    Thanx. I had actually determined I was gonna figure this out on my own and do some more digging and realized that just as "extern int foo=0" was wrong (which I'd already seen elsewhere) using "extern fooFunction()" was also wrong and to leave out the () completely (not just leave them empty.) So once I'd realized that and that I finally got it to compile cleanly, I came back here to see if anyone had responded in here. – Mike Anderson Apr 02 '19 at 17:08