PDA

View Full Version : Link fails using CMake if Q_OBJECT is present



PierreA
15th July 2017, 01:46
This is jwidget.h:

#include <QtWidgets>
#include "dotcanvas.h"
#include "juliaset.h"

class JuliaWidget: public QWidget
{
Q_OBJECT
public:
JuliaWidget(QWidget *parent=0);
~JuliaWidget();
public slots:
void setAngle(int newAngle);
void setSpeed(int sliderPos);
void stepAngle();
private:
DotCanvas *dotcanvas;
QSlider *speedSlider;
QVBoxLayout *layout;
QTimer *jTimer;
JuliaSet jset;
int speed,angle;
QTime lastTime;
};
This is jwidget.cpp:

#include <iostream>
#include <cmath>
#include "jwidget.h"
#include "angle.h"
#include "pncode.h"
using namespace std;

JuliaWidget::JuliaWidget(QWidget *parent):QWidget(parent)
{
resize(320,240);
setWindowTitle(tr("Julia Skein"));
show();
layout=new QVBoxLayout;
dotcanvas=new DotCanvas(this);
speedSlider=new QSlider(Qt::Horizontal,this);
jTimer=new QTimer(this);
speedSlider->setRange(0,21*12*16); // 21 octaves, highest speed is about 1 Hz
speedSlider->setSingleStep(16);
speedSlider->setPageStep(12*16);
jTimer->start(1000);
lastTime.start();
layout->addWidget(speedSlider);
layout->addWidget(dotcanvas);
setLayout(layout);
speedSlider->show();
dotcanvas->show();
setAngle(DEG120);
connect(speedSlider,&QSlider::valueChanged,this,&JuliaWidget::setSpeed);
connect(jTimer,&QTimer::timeout,this,&JuliaWidget::stepAngle);
}

JuliaWidget::~JuliaWidget()
{
delete jTimer;
delete speedSlider;
delete dotcanvas;
delete layout;
}

void JuliaWidget::setAngle(int newAngle)
{
vector<complex<double> > dots;
int i;
jset.setAngle(newAngle);
while (!jset.isReady())
jset.step();
for (i=0;i<PNLEN;i++)
{
jset.step();
dots.push_back(jset.getZ());
}
dotcanvas->setDots(dots);
dotcanvas->update();
}

void JuliaWidget::setSpeed(int sliderPos)
{
double rSpeed;
rSpeed=exp2(sliderPos/192.);
if (rSpeed>2147483647)
rSpeed=2147483647;
if (rSpeed<1)
rSpeed=1;
speed=rint(rSpeed);
cout<<"sliderPos "<<sliderPos<<" speed "<<speed<<endl;
rSpeed=exp2(10-sliderPos/384.);
if (rSpeed>2000)
rSpeed=2000;
jTimer->setInterval(rint(rSpeed));
}

void JuliaWidget::stepAngle()
{
angle+=lastTime.restart()*speed;
setAngle(angle);
}
This is CMakeLists.txt:

project(juliaskein)
cmake_minimum_required(VERSION 2.8.11)
add_executable(juliaskein juliaskein.cpp jwidget.cpp dotcanvas.cpp xy.cpp
angle.cpp juliaset.cpp pncode.cpp)
target_link_libraries(juliaskein Qt5::Widgets)

include(CheckTypeSize)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

find_package(Qt5Widgets)

set(CMAKE_CXX_FLAGS "-std=c++11")
install(TARGETS juliaskein DESTINATION bin)

set(JULIASKEIN_MAJOR_VERSION 0)
set(JULIASKEIN_MINOR_VERSION 1)
set(JULIASKEIN_PATCH_VERSION 0)
set(JULIASKEIN_VERSION ${JULIASKEIN_MAJOR_VERSION}.${JULIASKEIN_MINOR_VER SION}.${JULIASKEIN_PATCH_VERSION})

include_directories(${PROJECT_BINARY_DIR})
configure_file (config.h.in config.h)

set(CPACK_PACKAGE_VERSION_MAJOR ${JULIASKEIN_MAJOR_VERSION})
set(CPACK_PACKAGE_VERSION_MINOR ${JULIASKEIN_MINOR_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH ${JULIASKEIN_PATCH_VERSION})
set(CPACK_SOURCE_IGNORE_FILES /\\\\.git;.*~)
include(CPack)
If I compile, I get this:

CMakeFiles/juliaskein.dir/jwidget.cpp.o: In function `JuliaWidget::JuliaWidget(QWidget*)':
/home/phma/src/juliaskein/jwidget.cpp:8: undefined reference to `vtable for JuliaWidget'
/home/phma/src/juliaskein/jwidget.cpp:8: undefined reference to `vtable for JuliaWidget'
CMakeFiles/juliaskein.dir/jwidget.cpp.o: In function `JuliaWidget::~JuliaWidget()':
/home/phma/src/juliaskein/jwidget.cpp:32: undefined reference to `vtable for JuliaWidget'
/home/phma/src/juliaskein/jwidget.cpp:32: undefined reference to `vtable for JuliaWidget'
CMakeFiles/juliaskein.dir/jwidget.cpp.o: In function `JuliaWidget::tr(char const*, char const*, int)':
/home/phma/src/juliaskein/jwidget.h:7: undefined reference to `JuliaWidget::staticMetaObject'
collect2: error: ld returned 1 exit status
CMakeFiles/juliaskein.dir/build.make:253: recipe for target 'juliaskein' failed
make[2]: *** [juliaskein] Error 1
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/juliaskein.dir/all' failed
make[1]: *** [CMakeFiles/juliaskein.dir/all] Error 2
Makefile:149: recipe for target 'all' failed
make: *** [all] Error 2
The program compiles and runs fine if the Q_OBJECT line is removed. What am I missing?

high_flyer
15th July 2017, 23:46
You are not moc'ing your QObject headers (in this case the JuliaWidget header).
You have to add moc'ing to your CMakeLists.txt, and, the resulting cpp files to the build.

PierreA
16th July 2017, 04:15
But I already had an automoc line — why wasn't it moccing?
I appear to have figured it out. I rearranged CMakeLists to this:

cmake_minimum_required(VERSION 3.1.0)
project(juliaskein)

include(CheckTypeSize)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

find_package(Qt5Widgets)

add_executable(juliaskein juliaskein.cpp jwidget.cpp dotcanvas.cpp xy.cpp
angle.cpp juliaset.cpp pncode.cpp)
target_link_libraries(juliaskein Qt5::Widgets)

set(CMAKE_CXX_FLAGS "-std=c++11")
install(TARGETS juliaskein DESTINATION bin)

set(JULIASKEIN_MAJOR_VERSION 0)
set(JULIASKEIN_MINOR_VERSION 1)
set(JULIASKEIN_PATCH_VERSION 0)
set(JULIASKEIN_VERSION ${JULIASKEIN_MAJOR_VERSION}.${JULIASKEIN_MINOR_VER SION}.${JULIASKEIN_PATCH_VERSION})

include_directories(${PROJECT_BINARY_DIR})
configure_file (config.h.in config.h)

set(CPACK_PACKAGE_VERSION_MAJOR ${JULIASKEIN_MAJOR_VERSION})
set(CPACK_PACKAGE_VERSION_MINOR ${JULIASKEIN_MINOR_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH ${JULIASKEIN_PATCH_VERSION})
set(CPACK_SOURCE_IGNORE_FILES /\\\\.git;.*~)
include(CPack)
And I got these lines when compiling:

Scanning dependencies of target juliaskein_automoc
[ 10%] Automatic moc for target juliaskein
Generating moc_jwidget.cpp
[ 10%] Built target juliaskein_automoc
which weren't there before.

So why was the program running without moc and without the Q_OBJECT line? The program's window consists of two widgets. One displays an animated Julia set consisting of dots that move around; the other is a slider that controls the speed. The two widgets are connected by a signal and slot, which was working even without Q_OBJECT.

high_flyer
16th July 2017, 22:53
I admit I didn't notice the automoc line in your CMakeLists.txt, it was just clear to me that the problem was with moc'ing based on the symptoms.
Maybe the moc was running, but the results are not integrated in your build?
AUOTMOC doc says:

The global property AUTOGEN_TARGETS_FOLDER can be used to group the automoc targets together in an IDE, e.g. in MSVS.
Check to see if this var is set, if not set it, see if it helps.


So why was the program running without moc and without the Q_OBJECT line?
This as you describe as such is not possible.
If the problem was running and the signal and slots were working, then Q_OBJECT was being compiled, and the code was moc'ed.
It is more likely that what you think you had as a setup was wrong.

If the program build and runs with out Q_OBJECT and without mocing, simply continue programming without the Q_OBJECT and moc'ing, your error will show it self soon enough to explain why you thought it was working without them.