Its been a while since ive coded in c++/c and im a little confused as to what im doing wrong
I have three files, 2 .h and one .cpp.
The files are as follow
Base Menu
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
#define SSD1306_LCDHEIGHT 64
Adafruit_SSD1306 display(OLED_RESET);
class BaseMenu {
public:
// for moving the menu up and down
void moveDownMenu();
void moveUpMenu();
void selectMenu();
// for changing the text and the positions
void incrementText();
void decrementText();
protected:
//
char menuTitlePos1[20];
char menuTitlePos2[20];
char menuTitlePos3[20];
bool is_menuPos1_selected = true;
bool is_menuPos2_selected = false;
bool is_menuPos3_selected = false;
private:
virtual void AbstractClass() = 0;
};
MainMenu.h
#include "BaseMenu.h"
#include <string.h>
#define ARRAYSIZE 4
class MainMenu : BaseMenu {
private:
// Constructor
MainMenu() {};
public:
static void init();
};
MainMenu.cpp
#include "MainMenu.h"
void MainMenu::init() {
// clearing display
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(WHITE);
for (int i = 0; i < ARRAYSIZE; i++) {
if (i == 1) {
display.setTextSize(1);
display.setTextColor(BLACK, WHITE);
//display.println(MainMenuTitles[i]);
}
else {
display.setTextSize(1);
display.setTextColor(WHITE);
//display.println(MainMenuTitles[i]);
}
}
display.display();
}
When ever i go to build the code i get the following error.
MainMenu.cpp.o (symbol from plugin)*: In function display
(.text+0x0)*: multiple definition of display
TestingLibrary.cpp.o (symbol from plugin)*: (.text+0x0): first defined here
collect2.exe*: error: ld returned 1 exit status
Can someone please shed some light as to what i'm doing wrong?
Related
I am having issues with some code. In the CrossingSensor::loop() function, there is a line with: sonar.ping_timer(readSensor);.
I can call that fine, but in that function I can't access the status variable. What am I missing?
Error: error: 'status' was not declared in this scope status = on;
/*
Name
*/
#ifndef CrossingSensor_h
#define CrossingSensor_h
#include "Arduino.h"
class CrossingSensor
{
enum Status {off, on};
public:
CrossingSensor();
void init();
void loop();
void start();
void stop();
Status status;
private:
unsigned long _startMillis;
};
#endif
/*
NLE
*/
#include "Arduino.h"
#include "CrossingSensor.h"
#include <NewPing.h>
NewPing sonar(2,3,6);//trigger, echo, max distance
unsigned int pingSpeed = 500;// 50ms would be 20 times a second
unsigned long pingTimer;// Holds the next ping time
CrossingSensor::CrossingSensor()
{
Serial.println("CrossingSensor: Init");
//init();
}
void CrossingSensor::init()
{
Serial.println("CrossingSensor: Init/Attach/Reset");
pingTimer = millis();
}
void readSensor()
{
if (sonar.check_timer())
{
int cm = sonar.ping_result / US_ROUNDTRIP_CM;
if(cm > 0 && cm < 10)
{
//status = on;
}
else
{
//status = off;
}
}
}
void CrossingSensor::loop()
{
if (millis() >= pingTimer)
{
pingTimer += pingSpeed;
sonar.ping_timer(readSensor);
}
}
void CrossingSensor::start()
{
if(status == off)
{
Serial.println("CrossingSensor: Turning Sensor On");
status = on;
}
}
void CrossingSensor::stop()
{
if(status == on)
{
Serial.println("CrossingSensor: Turning Sensor Off");
status = off;
}
}
You never declared the function readSensor() as a part of CrossingSensor. Since it isn't a member function, it has no access to status.
All you should have to do is declare the function as a member of CrossingSensor or pass it a CrossingSensor object.
You would also need to change the .cpp file to reflect that change. The declaration for each would look like the following:
class CrossingSensor
{
enum Status {off, on};
public:
CrossingSensor();
void init();
void readSensor();
void loop();
void start();
void stop();
Status status;
private:
unsigned long _startMillis;
};
#endif
This is your method declaration:
void CrossingSensor::readSensor()
{
// your code here
}
This makes readSensor a part of CrossingSensor as well as the other way around.
The readSensor() function isn't part of CrossingSensor so it can't see CrossingSensor's status.
Make it part of Crossingsensor by declaring it there in your header file, and then define it as void CrossingSensor::readSensor(), in your code, and not as void readSensor().
Also, I don't know what sonar.ping_timer() takes as an argument, but I doubt it is readSensor. You will have to find another way of passing status to sonar.ping_timer(), if that is what you intended to do.
I've recently started learning C++/Arduino and am working on abstracting some of my Arduino code in order to keep it more manageable.
I'm trying to construct a class with 2 arrays as attributes on it, one to store strings that represent commands, and a second one to store pointers to those functions.
The below code works (compiles), but when uploaded to the device both the listen and execute functions don't appear to work. I've searched around quite a lot, but can't find where I've gone wrong.
/* main.ino */
// SETUP
#include "SoftwareSerial.h"
SoftwareSerial bt(btRx, btTx);
#include "CMD.h"
const int cmdMax = 6;
ArriCMD cmd;
// COMMANDS
void cmdStatus()
{
Serial.println("OK");
}
// START
void setup()
{
Serial.begin(9600);
bt.begin(9600);
cmd.add("AH+STAT", cmdStatus);
}
void loop()
{
cmd.listen(bt);
}
/* ArriCMD.h */
#ifndef ArriCMD_h
#define ArriCMD_h
#include "Arduino.h"
#include "SoftwareSerial.h"
class ArriCMD
{
public:
ArriCMD();
void add(String cmd, void (*cb)());
void listen(SoftwareSerial serial);
void execute(String cmd);
private:
int _max = 16;
int _amt = 0;
String _cmds[16];
void (*_cbs[16])();
};
/* ArriCMD.cpp */
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "ArriCMD.h"
ArriCMD::ArriCMD()
{
//
}
void ArriCMD::add(String cmd, void (*cb)())
{
if (_amt < _max) {
_cmds[_amt] = cmd;
_cbs[_amt] = *cb;
}
}
void ArriCMD::listen(SoftwareSerial serial)
{
if (serial.available()) {
String cmd = serial.readString();
Serial.print(cmd);
execute(cmd);
}
}
void ArriCMD::execute(String cmd)
{
for (int i = 0; i < _amt; i++) {
if (cmd == _cmds[i]) {
_cbs[i]();
}
}
}
While I've been programming for over a decade, C++ and microcontrollers are brand new to me, any and all help here would be hugely appreciated.
I do intend to open source these libraries, and the subsequent platforms they're built for, once I'm more comfortable with my code quality.
Looks like you forgot to increment your commands counter _amt
void ArriCMD::add(String cmd, void (*cb)())
{
if (_amt < _max) {
_cmds[_amt] = cmd;
_cbs[_amt] = *cb;
_amt++; // <-- here, don't you need it?
}
}
Apart from that, is there some particular reason for using raw array and raw function pointers in your code? I do not use Arduino, so I am not sure, but maybe this solution is a bit cleaner:
class ArriCMD
{
public:
ArriCMD();
void add(String cmd, std::function<void()> cb);
void listen(SoftwareSerial serial);
void execute(String cmd);
private:
std::map<String, std::function<void()> > _cmdMap;
};
I'm attempting to break out a previously working sketch I created into a class and sketch, and I'm having some trouble. I'm extending the functionality of a keyboard class designed for the board I'm working on, but the compiler keeps complaining about multiple definitions of functions.
here is my sketch, my .h, and my .cpp:
sketch
#include <DigiKeyboard.h>
#include "MacroKeyboard.h"
//constants
#define LED 1
#define SWITCH 0
//configuration
#define PAUSE_ON_ENTER true
const char command[] PROGMEM = "test string";
MacroKeyboard kb(command, SWITCH);
void setup() {
//tie switch to high
pinMode(SWITCH, OUTPUT);
digitalWrite(SWITCH, HIGH);
// set pins to correct uh whatever
pinMode(LED, OUTPUT);
pinMode(SWITCH, INPUT);
}
void loop() {
DigiKeyboard.update();
kb.update();
}
.h:
#ifndef MacroKeyboard_h
#define MacroKeyboard_h
#include "Arduino.h"
#define ON LOW
#define OFF HIGH
#define SCANCODE_DIFF 8
class MacroKeyboard {
const char* command;
int pin;
bool isPressed = false;
public:
MacroKeyboard(const char*, int);
void parseLetter(int);
void update();
};
#endif
.cpp:
#include <DigiKeyboard.h>
#include "MacroKeyboard.h"
MacroKeyboard::MacroKeyboard(const char* macro_position, int pin_number){
command = macro_position;
pin = pin_number;
}
void MacroKeyboard::parseLetter(int letter){
unsigned int scancode = pgm_read_byte_near(ascii_to_scan_code_table + letter - SCANCODE_DIFF);
unsigned int keycode = scancode & ((1 << 7) - 1); // masking off shift bit
unsigned int mods = 0;
unsigned int shift = (scancode & (1 << 7)) >> 7;
mods |= (shift == 1) ? MOD_SHIFT_LEFT : 0;
DigiKeyboard.sendKeyStroke(keycode, mods);
}
void MacroKeyboard::update(){
if(digitalRead(pin) == ON && !isPressed){
isPressed = true;
for (int i = 0; i < strlen(command); i++){
char letter = pgm_read_byte_near(command + i);
parseLetter(letter);
}
}
if (digitalRead(pin) == OFF && isPressed){
isPressed = false; // debounce
}
}
the errors I'm getting are this:
MacroKeyboard\MacroKeyboard.cpp.o: In function `usbFunctionSetup':
C:\Program Files (x86)\Digistump_Arduino\hardware\digistump\avr\libraries\DigisparkKeyboard/DigiKeyboard.h:216: multiple definition of `usbFunctionSetup'
MacroKeyboardTest.cpp.o:C:\Program Files (x86)\Digistump_Arduino\hardware\digistump\avr\libraries\DigisparkKeyboard/DigiKeyboard.h:216: first defined here
MacroKeyboard\MacroKeyboard.cpp.o: In function `MacroKeyboard::MacroKeyboard(char const*, int)':
C:\Program Files (x86)\Digistump_Arduino\hardware\digistump\avr\libraries\DigisparkKeyboard/DigiKeyboard.h:170: multiple definition of `DigiKeyboard'
MacroKeyboardTest.cpp.o:C:\Program Files (x86)\Digistump_Arduino\hardware\digistump\avr\libraries\DigisparkKeyboard/DigiKeyboard.h:199: first defined here
MacroKeyboard\MacroKeyboard.cpp.o:(.progmem.data.usbDescriptorHidReport+0x0): multiple definition of `usbDescriptorHidReport'
MacroKeyboardTest.cpp.o:(.progmem.data.usbDescriptorHidReport+0x0): first defined here
collect2: error: ld returned 1 exit status
Error compiling.
what am I doing wrong?
The library you use is written in a strange way. In the file DigiKeyboard.h there are such definitions as (comments mine):
// Declares a global variable.
// If you include this file in more than one cpp file it will cause linker error.
DigiKeyboardDevice DigiKeyboard = DigiKeyboardDevice();
// Defines a non-inline function
// Again, multiple definitions error if the file is included in several source files.
uchar usbFunctionSetup(uchar data[8]) {
// code
}
So you have to use it by including this .h file into only one of .cpp files.
I have a Marmalade C++ project where the built in font doesn't scale to screen size. To deal with this issue I'm making a custom font, which is basically a png and a .xml file, containing the (0,0) vector of each character, and each character's size. I'm then creating an instance of the png, and drawing regions of it in a for loop to write on the screen.
The issue I'm having is the scope of this class. I want it to be used throughout the entire project. So the static DebugLogger would use this font, any UI's, or GameObjects, anything which need characters, would use this font. Almost like it's pre-built into the system.
The Logger in this project is a static class, with static functions. The custom font class I currently have is a Singleton, as is my understanding, it must be an instantiated object for the program to draw images of it. So I did not make it static. Is there a clear cut answer to this type of problem? The issue is the static class won't create a reference to a singleton, and I've been told the singleton is to be avoided from a design perspective anyway!
Thanks in advance guys!
Here's the code for reference:
CustomFontClass
#ifndef _CHARACTER_SET_H_
#define _CHARACTER_SET_H_
#include "Iw2D.h"
#include "Singleton.h"
#include "BaseImage.h"
#include <map>
#include <string>
typedef CIwArray<std::string> STR_ARR;
class CharacterSet : public Singleton<CharacterSet>
{
public:
CharacterSet();
void Write(std::string, CIwFVec2, float);
void Write(std::string, float, float, float);
void Write(char, CIwFVec2, float);
void Write(char, float, float, float);
bool IsInitialised() const { return mIsInitialised; }
// Space between characters is equivalent to 1 character width. This float multiplies that number. Default is .55f
void SetFontSpacing(float);
protected:
BaseImage* mspCharacters;
bool Initialise();
private:
std::map<const char, CIwFVec2> mFontMap;
static const std::string TEXTURE_PATH;
static const std::string TEXTURE_NAME;
static const CIwFVec2 mFontSize;
float mFontSpacing;
STR_ARR maTextureLocations;
bool mIsInitialised;
};
#define CHARACTERSET Singleton<CharacterSet>::GetInstance()
#endif
cpp
#include "Iw2D.h"
#include "Singleton.h"
#include "BaseImage.h"
#include "CharacterSet.h"
#include "IwLogger.h"
#include <map>
#include <sstream>
#include <string>
const std::string CharacterSet::TEXTURE_PATH = "textures/";
const std::string CharacterSet::TEXTURE_NAME = "font_main.png";
const CIwFVec2 CharacterSet::mFontSize = CIwFVec2(50, 63);
CharacterSet::CharacterSet() : Singleton<CharacterSet>()
{
mFontSpacing = 0.55f;
mIsInitialised = Initialise();
};
void CharacterSet::SetFontSpacing(float spacing)
{
if (spacing != NULL)
{
mFontSpacing = spacing;
}
};
void CharacterSet::Write(std::string text, CIwFVec2 position = CIwFVec2(50,50), float fontSize = 25.0f)
{
if (!text.empty())
{
mspCharacters->SetSize(CIwFVec2(fontSize, fontSize));
float newPosition = 0;
for (char& c : text)
{
mspCharacters->SetPosition(CIwFVec2((position.x + newPosition), position.y));
if (mFontMap.find(c) == mFontMap.end())
{
std::stringstream ss;
std::string mCharRef;
ss << c;
ss >> mCharRef;
std::string error = "CharacterSet::mFontMap[" + mCharRef + "] - Does not exist!";
IW_LOG_ERROR(error);
}
else
{
mspCharacters->Render(mFontMap[c], mFontSize);
}
newPosition += (fontSize * mFontSpacing);
}
}
else
{
IW_LOG_ERROR("CharacterSet::Write - std::string text is empty!");
}
};
bool CharacterSet::Initialise()
{
maTextureLocations.push_back(std::string(TEXTURE_PATH + TEXTURE_NAME));
mspCharacters = new BaseImage(maTextureLocations[0].c_str());
if (mspCharacters == NULL)
{
IW_LOG_ERROR("CharacterSet::mspCharacters - Failed to create BaseImage!");
return false;
}
//Numerical symbols
mFontMap['1'] = CIwFVec2(0, 0);
mFontMap['2'] = CIwFVec2(50, 0);
mFontMap['3'] = CIwFVec2(100, 0);
mFontMap['4'] = CIwFVec2(150, 0);
mFontMap['5'] = CIwFVec2(200, 0);
// ect... lots of these
IwLogger
#ifndef _IW_LOGGER_H_
#define _IW_LOGGER_H_
#include "IwDebug.h"
#include <string>
class IwLogger
{
public:
IwLogger();
enum LogLevel
{
INFO,
WARN,
ERROR
};
static void Log(LogLevel level, std::string msg);
static void Log(LogLevel level, std::string callingFunc, std::string msg);
static void LogFailedResource(std::string callingFunc, std::string resourcePath);
static void LogError(std::string msg) { Log(ERROR, msg); }
static void LogWarn(std::string msg) { Log(WARN, msg); }
static void LogInfo(std::string msg) { Log(INFO, msg); }
static void LogError(std::string callingFunc, std::string msg) { Log(ERROR, callingFunc, msg); }
static void LogWarn(std::string callingFunc, std::string msg) { Log(WARN, callingFunc, msg); }
static void LogInfo(std::string callingFunc, std::string msg) { Log(INFO, callingFunc, msg); }
};
#define IW_LOG_ERROR(msg) IwLogger::LogError(__FUNCTION__, msg)
#define IW_LOG_WARN(msg) IwLogger::LogWarn(__FUNCTION__, msg)
#define IW_LOG_INFO(msg) IwLogger::LogInfo(__FUNCTION__, msg)
#define IW_LOG_FAILED_RES(resPath) IwLogger::LogFailedResource(__FUNCTION__, resPath)
#endif
cpp
#include "CharacterSet.h"
#include "IwLogger.h"
void IwLogger::Log(IwLogger::LogLevel level, std::string msg)
{
switch (level)
{
case INFO:
IwTrace(INFO, (msg.c_str()));
break;
case WARN:
IwTrace(WARN, (msg.c_str()));
break;
case ERROR:
IwTrace(ERROR, (msg.c_str()));
break;
default:
IwTrace(ERROR, ("IwLogger::Log() - Uncatered for case! Nothing to log!"));
break;
}
}
void IwLogger::Log(IwLogger::LogLevel level, std::string callingFunc, std::string msg)
{
std::string finalMsg = callingFunc + "() - " + msg;
switch (level)
{
case INFO:
IwTrace(INFO, (finalMsg.c_str()));
break;
case WARN:
IwTrace(WARN, (finalMsg.c_str()));
break;
case ERROR:
IwTrace(ERROR, (finalMsg.c_str()));
break;
default:
IwTrace(ERROR, ("IwLogger::Log() - Uncatered for case! Nothing to log!"));
break;
}
}
void IwLogger::LogFailedResource(std::string callingFunc, std::string resourcePath)
{
std::string msg = "Failed to load resource: " + resourcePath;
LogError(callingFunc, msg);
}
The error messages:
1>c:\users\...\source\framework\customfont.h(12): error C2504: 'Singleton' : base class undefined (..\source\framework\BaseObject.cpp)
1>c:\users\...\source\framework\customfont.h(12): error C2143: syntax error : missing ',' before '<' (..\source\framework\BaseObject.cpp)
1>c:\users\nigel\...\source\framework\customfont.h(12): error C2504: 'Singleton' : base class undefined (..\source\framework\CustomFont.cpp)
moc_mm.o: In function `Counter::metaObject() const':
moc_mm.cpp:(.text+0x0): multiple definition of `Counter::metaObject() const'
moc_joystick.o:moc_joystick.cpp:(.text+0x0): first defined here
moc_mm.o:(.rodata+0x0): multiple definition of `Counter::staticMetaObject'
Which part of the code am I supposed to present here, w.r.t this error?
What is meaning of this error?
I have done make clean, but that doesn't help. The function name metaObject seems to be internal to Qt.
EDIT 1:
Adding the header file:
#include <stdio.h>
#include <QObject>
#ifndef __JOYSTICK_H__
#define __JOYSTICK_H__
#define JOYSTICK_DEVNAME "/dev/input/js0"
#define JS_EVENT_BUTTON 0x01 /* button pressed/released */
#define JS_EVENT_AXIS 0x02 /* joystick moved */
#define JS_EVENT_INIT 0x80 /* initial state of device */
struct js_event {
unsigned int time; /* event timestamp in milliseconds */
short value; /* value */
unsigned char type; /* event type */
unsigned char number; /* axis/button number */
};
struct wwvi_js_event {
int button[11];
int stick_x;
int stick_y;
};
class Counter : public QObject
{
Q_OBJECT
private:
int m_value;
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
//public slots:
int open_joystick();
int read_joystick_event(struct js_event *jse);
//void set_joystick_y_axis(int axis);
//void set_joystick_x_axis(int axis);
void close_joystick();
int get_joystick_status(struct wwvi_js_event *wjse);
//signals:
//void valueChanged(int newValue);
};
#endif
Output:
anisha#linux-dopx:~/Desktop/anishaJoystick> qmake -project
anisha#linux-dopx:~/Desktop/anishaJoystick> qmake
anisha#linux-dopx:~/Desktop/anishaJoystick> make
g++ -m64 -Wl,-O1 -Wl,-rpath,/home/anisha/qtsdk-2010.05/qt/lib -o anishaJoystick joy.o mm.o moc_joystick.o moc_mm.o -L/home/anisha/qtsdk-2010.05/qt/lib -lQtGui -L/home/anisha/qtsdk-2010.05/qt/lib -L/usr/X11R6/lib64 -lQtCore -lpthread
moc_mm.o: In function `Counter::metaObject() const':
moc_mm.cpp:(.text+0x0): multiple definition of `Counter::metaObject() const'
moc_joystick.o:moc_joystick.cpp:(.text+0x0): first defined here
moc_mm.o:(.rodata+0x0): multiple definition of `Counter::staticMetaObject'
moc_joystick.o:(.rodata+0x0): first defined here
moc_mm.o: In function `Counter::qt_metacast(char const*)':
moc_mm.cpp:(.text+0x20): multiple definition of `Counter::qt_metacast(char const*)'
moc_joystick.o:moc_joystick.cpp:(.text+0x30): first defined here
moc_mm.o: In function `Counter::valueChanged(int)':
moc_mm.cpp:(.text+0x70): multiple definition of `Counter::valueChanged(int)'
mm.o:mm.cpp:(.text+0x10): first defined here
moc_mm.o: In function `Counter::qt_metacall(QMetaObject::Call, int, void**)':
moc_mm.cpp:(.text+0xb0): multiple definition of `Counter::qt_metacall(QMetaObject::Call, int, void**)'
moc_joystick.o:moc_joystick.cpp:(.text+0x20): first defined here
collect2: ld returned 1 exit status
make: *** [anishaJoystick] Error 1
Source file:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "joystick.h"
static int joystick_fd = -1;
int Counter::open_joystick()
{
joystick_fd = open(JOYSTICK_DEVNAME, O_RDONLY | O_NONBLOCK); // read write for force feedback?
if (joystick_fd < 0)
return joystick_fd;
// maybe ioctls to interrogate features here?
return joystick_fd;
}
int Counter::read_joystick_event(struct js_event *jse)
{
int bytes;
bytes = read(joystick_fd, jse, sizeof(*jse));
if (bytes == -1)
return 0;
if (bytes == sizeof(*jse))
return 1;
printf("Unexpected bytes from joystick:%d\n", bytes);
return -1;
}
void Counter:: close_joystick()
{
close(joystick_fd);
}
int Counter::get_joystick_status(struct wwvi_js_event *wjse)
{
int rc;
struct js_event jse;
if (joystick_fd < 0)
return -1;
// memset(wjse, 0, sizeof(*wjse));
while ((rc = read_joystick_event(&jse) == 1)) {
jse.type &= ~JS_EVENT_INIT; /* ignore synthetic events */
if (jse.type == JS_EVENT_AXIS) {
switch (jse.number) {
case 0: wjse->stick_x = jse.value;
break;
case 1: wjse->stick_y = jse.value;
break;
default:
break;
}
} else if (jse.type == JS_EVENT_BUTTON) {
if (jse.number < 10) {
switch (jse.value) {
case 0:
case 1: wjse->button[jse.number] = jse.value;
break;
default:
break;
}
}
}
}
// printf("%d\n", wjse->stick1_y);
return 0;
}
//#if 0
/* a little test program */
int main(int argc, char *argv[])
{
int fd, rc;
int done = 0;
struct js_event jse;
Counter c;
fd = c.open_joystick();
if (fd < 0) {
printf("open failed.\n");
exit(1);
}
while (!done) {
rc = c.read_joystick_event(&jse);
usleep(1000);
if (rc == 1) {
printf("Event: time %8u, value %8hd, type: %3u, axis/button: %u\n", jse.time, jse.value, jse.type, jse.number);
}
}
}
//#endif
You probably have multiple moc_* files, perhaps old ones after some renames, in your directory, and running qmake -project has included the old ones in your build. It finds multiple moc_* declarations for the same names (moc_mm.cpp and moc_joystick.cpp) and conflicts.
Remove all moc_* files by hand and re-create the .pro file with -project.
This error also happens when you accidentally use the Q_OBJECT macro twice for the same class.
For example, I just had this during a messed up transition from a source code layout with declaration and implementation in the .cpp file (as described here) to the typical layout of splitting these into .h and .cpp. Mapped to your example code, my code was as follows:
joystick.h: Exactly as in the question, including this key section with the macro call:
class Counter : public QObject {
Q_OBJECT
// ... method declarations here ...
};
joystick.cpp, including the accidental second macro call from leftovers of the original implementation that was completely here:
class Counter : public QObject {
// The erroneous SECOND call of the macro.
Q_OBJECT
// ... method definitions here ...
};
// The obligatory include when using the Q_OBJECT macro in a .cpp file.
#include "joystick.moc"