Использование extern и предотвращение дублирования определений - PullRequest
1 голос
/ 02 апреля 2019

Я переписываю это с фактическими файлами, которые я использовал (но сильно раздетый. И мне нужно прекратить публиковать, когда я полусонный.)

Test.hpp читает:

/*
 * 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 читается как:

#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 читает

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

#include "test.hpp";

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

При компиляции вышеизложенного возникает следующая ошибка:

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

Если я пытаюсь добавить три строки с 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);

в файлы test.hpp и test2.cpp, а затем в test.cpp без extern, я получаю эту ошибку:

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 

Поместив эти три в test.hpp без extern, а затем поместив их в test.cpp и test2.cpp с extern, я получаю примерно ту же ошибку.

1 Ответ

2 голосов
/ 02 апреля 2019

Вы хотите одно объявление с extern в заголовке. Просто декларация. Так в Test.hpp

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

становится

extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8;

Обязательная стандартная цитата

Когда вы прикрепляете extern к нему и оставляете инициализацию,

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

extern фактически игнорируется, и вместо объявления вы получаете определение . Компилятор может предупредить вас об этом, и он сделал

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

Но если вы не знаете, что ищете, сообщение не сильно поможет. В любом случае, не игнорируйте предупреждения. Это компилятор, который говорит вам, что хотя что-то компилируется (это синтаксически правильно), это, вероятно, не означает, что вы хотите или делаете то, что вы хотите (это, вероятно, логически неверно). Вы можете обнаружить, что предупреждения часто содержат больше полезной информации, чем фактическая ошибка, которая возникает, поэтому выясните, что они означают, и устраните их.

Возвращаясь к теме,

extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8;

в test.hpp обещает, что u8x8 был или будет определен где-то еще и безопасен для использования. Следующий шаг сдерживает обещание. В test.cpp XOR test2.cpp (один из двух, а не оба) добавьте

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

Сделайте то же самое для button и encoder.

Подробнее

Когда использовать extern в C ++

и C, а не C ++, но C и C ++ здесь достаточно близки, чтобы это длинное обсуждение было полезным: Как использовать extern для обмена переменными между исходными файлами?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...