Qt debug error (variable) not accessible - c++

I have encountered a runtime error which while debugging list a variable as not accessible. there are other post on this subject but the fixes do not work for me.
I use QtCreator 4.5.1 and Qt lib 5.10 (Mingw32 5.3, MSVC14 x64, MSVC x64)
I have disabled "Load system GDB pretty printers" as suggested in
disable pretty printers post
.
Intent of failing code:
In order to communicate between my decoupled QtQuick2 viewmodels I implemented a simple version of the EventAgregator pattern.
the implementation is still missing thread safty and clean up of no longer existing subscribers. however it is not relevant for my error.
break point at error to be thrown and current watch list
relevant code files:
qspinstartup.h
class QSpinStartup:QObject
{
public:
QSpinStartup();
bool loadQSpin(const QString dir, const QString startView="");
private:
void registerQmlGui();
void registerQmlViewModel();
QQmlApplicationEngine engine;
//constant viewmodel objects
CodeEditorTab* codeEditor;
VerificationTab* verificationEditor;
QSpinMessageService msgService;
};
qspinstartup.cpp
QSpinStartup::QSpinStartup():QObject()
,verificationEditor(new VerificationTab(msgService,this))
,codeEditor(new CodeEditorTab(msgService,this))
//,console(new ConsoleEditor(msgService,this))
{
registerQmlGui();
registerQmlViewModel();
initPointerObjects();
auto ctx = engine.rootContext();
qSpinNullPtrCheck(ctx);
PrintToConsole p("pri");
msgService.publish<PrintToConsole>(p);
ctx->setContextProperty("codeEditorVm",codeEditor);
}
bool QSpinStartup::loadQSpin(const QString dir,const QString startView)
{
QDir ldir(dir);
QString l = ldir.entryList(QStringList()<<startView,QDir::Files).first();
qInfo()<<l;
engine.load(QUrl(l.prepend("qrc:/")));
bool isReady = engine.rootObjects().count()>0;
qSpinSafeFail(!isReady,"Qml Application failed to start engine");
return isReady;
}
void QSpinStartup::registerQmlGui()
{
QSpinWorkSpace::Workspace::registerAsQml();
}
void QSpinStartup::registerQmlViewModel()
{
VerificationTab::registerAsQml();
CodeEditorTab::registerAsQml();
ConsoleEditor::registerAsQml();
}
ieventbase.h
class IEventBase{
public:
enum Types{ // <--simple way to give an object a type specifier
PrintToConsole,
CurrentDocumentChanged,
CompilerOptions,
SpinArgCommand
};
IEventBase(Types t):type(t){}
const Types type;
};
qspinmessageservice.h
template<class T>
class ISubscriber{
public:
virtual ~ISubscriber(){}
virtual void executeSubscribed(T& event)=0;
};
class QSpinMessageService: public QObject{
Q_OBJECT
typedef QPointer<QObject> subscriber;
typedef QVector<subscriber> EvAgrList;
QHash<IEventBase::Types,EvAgrList> subs;
public:
QSpinMessageService(QObject* parent=nullptr):QObject(parent)
,subs(QHash<IEventBase::Types,EvAgrList>()){}
void subscribe(QObject *obj, IEventBase::Types key){
if(!subs.contains(key)) // <-- this line cause my error
subs[key]= EvAgrList();
EvAgrList& list = subs[key];
list<<subscriber(obj);
}
template<typename T>
void publish(typename T::IEventBase execute){
EvAgrList lSubs = subs[execute.type];
for(int i=0; i<lSubs.count();i++){
subscriber&q = lSubs[i];
if(q.isNull()){
lSubs.removeOne(q);
--i;
continue;
}
ISubscriber<T>* a = dynamic_cast<ISubscriber<T>*>(q.data());
qDebug()<< q<<a;
//ISubscriber<T>* s = dynamic_cast<ISubscriber<T>*>(q);
a->executeSubscribed(static_cast<T&>(execute));
}
}
};
static QSpinMessageService dummy;
verificationtab.cpp (its a big class so i excluded irrelevant parts)
VerificationTab::VerificationTab(QSpinMessageService &service, QObject *parent) :
QSpinTab(parent,"Verification",QSpinTabType::Verification)
,msgService(service)
,m_stateProp(new StateSpaceProp(this)),m_stateSpec(new StateSpaceSpec(this))
,m_memoryUsage(new MemoryUsage(this)),m_settings(new VerificationSettings(this))
{
msgService.subscribe(this,IEventBase::CurrentDocumentChanged); // <-- error call
//if the line above is commented out the application runs
}
Github repository of the full application
note:
I Designed the QSpinMessageService in seperate empty project, before adding the files to the main project. the subcribe methods works in that project.
Any idea of how to fix this?

I finally located the cause of my error. Despite the fact that the Debug Watch list indicate That the QSpinMessageService object exist, It havent been propberly initialized yet at this point.
Why subscribe() is even able to be accessed is beyond my current cpp skills.
The fix:
In qspinstartup.cpp the construtor need to be changed as follows.
qspinstartup.cpp
QSpinStartup::QSpinStartup():QObject()
{
codeEditor = new CodeEditorTab(msgService,this);
verificationEditor = new VerificationTab(&msgService,this);
registerQmlGui();
registerQmlViewModel();
auto ctx = engine.rootContext();
qSpinNullPtrCheck(ctx);
PrintToConsole p("pri");
msgService.publish<PrintToConsole>(p);
ctx->setContextProperty("codeEditorVm",codeEditor);
}

Related

Call Method from QT slot in static method

I have a static method which shows a list (Items) and return an array of selected object in Items.
I would like to add a checkbox to reload the list along some parameters. We are using QT 3.3.
//// Static
int CMSUI_InputDialog::FittingList(QWidget* parent,
const CString& Title,
const CStringArray& Items,
bool IsMultiSelect,
int DefaultItem,
bool OkIsDefault,
CArray<int, int>& Selecteds)
{
int ret = 0;
Selecteds.RemoveAll();
/// Create grid
QDialog dialog(parent, 0, true);
dialog.setCaption(QString(Title));
QGridLayout* pLayoutGrid = Init(&dialog, OkIsDefault);
//Create checkBox
QCheckBox* pCheckBox = new QCheckBox(&dialog, "m_pApply_Filter");
pCheckBox->setText("Norm Filter");
pLayoutGrid->addWidget(pCheckBox, 0, 0);
QObject::connect(pCheckBox, SIGNAL(stateChanged(int)), &dialog, SLOT(checkboxClicked()));
/// Create ListBox
QListBox* pList = new QListBox(&dialog);
pList->setMinimumSize(QSize(370, 90));
pList->setSelectionMode(QListBox::Extended);
// Load the list
int Count = Items.GetSize();
for (int i = 0; i < Count; i++)
{
QString QS(Items[i]);
pList->insertItem(QS);
}
if (DefaultItem >= 0 && DefaultItem < Count)
pList->setSelected(DefaultItem, true);
pLayoutGrid->addWidget(pList, 1, 0);
// Connect double clic on QDialog accept
QObject::connect(pList, SIGNAL(doubleClicked(QListBoxItem*)), &dialog, SLOT(accept()));
if (dialog.exec() == QDialog::Accepted)
{
for (int i = 0; i < Count; i++)
{
if (pList->isSelected(i))
Selecteds.Add(i);
}
ret = 1;
}
return ret;
}
void CMSUI_InputDialog::checkboxClicked()
{
//To do
}
checkboxClicked() is declared in CMSUI_InputDialog.h as slot
class CMSUI_InputDialog
{
protected :
static QGridLayout* Init(QWidget* pParent, bool OkIsDefault);
public slots:
void checkboxClicked();
public:
/// CheckBox + ListBox for fittings
static int FittingList(QWidget* parent,
const CString& Title,
const CStringArray& Items,
bool IsMultiSelect,
int DefaultItem,
bool OkIsDefault,
CArray<int, int>& Selecteds);
};
I tried many things but I'm stupid with QT and didn't got any success to catch the action on the checkbox
There's little Qt-specific here as far as the primary problem goes: it's all understandable in plain C++, no need for Qt knowledge.
You're not checking the result returned by the connect method: it returns false when it fails. The connect fails in your case, so there's no point in going any further than that. The slot will not be called. The reason for that: dialog is just a QDialog, not CMSUI_InputDialog. You can't add any slots to an existing Qt class.
Slots must be methods in QObjects. The class where you added the "slot" is not derived from QObject. You'd need to create such a class (to replace the QDialog that you're using).
slots is a macro that is empty (expands to nothing) and thus has no effect on the compilation. It has a purpose, though: the moc (meta object compiler) that processes the header file will notice that macro and process the subsequent methods as slots. It won't do that unless you also have the Q_OBJECT macro within the class.
First, you'd want to factor out the options for that dialog into a FittingOptions struct, to make the code manageable.
struct FittingOptions {
const CStringArray& items,
bool isMultiSelect,
int defaultItem,
bool okIsDefault,
};
The dialog should become a new class, where your slot would go:
class FittingList : public QDialog {
Q_OBJECT
FittingOptions opt;
CArray<int, int>* selecteds;
public:
FittingList(QWidget *parent, const FittingOptions &options) :
QDialog(parent), opt(options), selecteds(0)
{
// TODO: initialization code that creates widgets etc.
}
void setSelecteds(CArray<int, int> &selecteds)
{
this->selecteds = &selecteds;
}
// ...
public slots:
void checkboxChanged() { /* TODO */ }
};
And then, put the initialization code from the FittingList method into the constructor. Change that method into:
int CMSUI_InputDialog::FittingList(QWidget* parent,
const CString& title,
const CStringArray& items,
bool isMultiSelect,
int defaultItem,
bool okIsDefault,
CArray<int, int>& selecteds)
{
const FittingOptions options = {
items, isMultiSelect, defaultItem, okIsDefault
};
::FittingList dialog(parent, options);
dialog.setCaption(QString(title));
dialog.setSelecteds(selecteds);
if (dialog.exec() != QDialog::Accepted) return 0;
return 1;
}
Look at other examples within your project to see how they went about such problems. This code is quite stale by today's standards, but it's a maintenance job as far as I understand, so you got to do more of the same - since I imagine you're not upgrading the Qt version.
Note: this is obviously something that concerns the original author(s) of the code, not you. You haven't written this stuff. Even in Qt 3's times this code would have been considered crusty - the seemingly pervasive use of globals/singletons is cringe-worthy. I always wonder why people who work on such presumably large scale projects won't look into the code they paid for and is available to them: Qt 3 includes full source code to Qt Designer, and that's where one might have looked for inspiration/tutoring. These days, Qt Creator is a much bigger code base than Designer (by more than an order of magnitude) and is fairly decently architected, so large-scale projects might take inspiration from there.

Qt 5.2 DLL Returns different results than Application calling the DLL

Summary : I have a DLL which exports a function. In this function I am calling QApplication::allWidgets() which returns an empty list whereas if this function is in the application returns a list with 19 items.
If I am not mistaken there is a static list of items maintained by the framework. allWidgets() function is returning that list. To my knowledge DLL is supposed to be in the same address space as the application which means I should be able to access all the static variables from DLL. Then why does it make a difference whether the function is in DLL vs application?
Here is DLL code :
------------------------ test.h ----------------
extern "C" TESTSHARED_EXPORT std::string DllFun();
------------------------ test.cpp --------------
std::string DllFun()
{
std::stringstream temp;
QList<QWidget *> list = QApplication::allWidgets();
temp << "Number of widgets from DLL " << list.size();
return temp.str();
}
Here is the application code snippet :
------------------------------ mainwindow.cpp ----------------
std::string MainWindow::FunInternal()
{
std::stringstream temp;
QList<QWidget *> list = QApplication::allWidgets();
temp << "Number of widgets from Applicaton " << list.size();
return temp.str();
}
void MainWindow::on_pushButton_clicked()
{
typedef std::string (*FunType)();
HMODULE handle = LoadLibrary(L"Test.dll");
FunType DllFun = (FunType)GetProcAddress(handle, "DllFun");
if (Fun)
{
std::string DllWidgets = DllFun();
std::string InternalWidgets = FunInternal();
ui->textEdit->append(QString(DllWidgets.c_str()));
ui->textEdit->append(QString("\n"));
ui->textEdit->append(QString(InternalWidgets.c_str()));
}
}
When I click the button results are :
Number of widgets from DLL 0
Number of widgets from Applicaton 19
I am using Qt 5.2 Mingw 64 Bit.
Update : Here is a workaround that appears to be working even though my question is still valid. If I pass a function pointer to the DLL and call the function pointer from DLL it returns 19 just like application.
As Chris pointed out DLL and Application appears to have two separate Data sections. DLL cannot normally access to the application's data section (which contains state information of the GUI) except through an interface to the data or a pointer to it. In my case this was not possible since my interface is generic. I tried to push this a bit by using GetProcAddress inside my DLL but I did not have much success but I think in theory this should work.
My current design is something like this :
------- genwidget.h
class MyWidget
{
public:
void modifyWidget(HWND id);
}
------- qtwidget.cpp
#include <genwidget.h>
void MyWidget::modifyWidget(HWND id)
{
// find Panel widget
QWidget *widget = find_widget_from_list(id, QApplication::allWidgets())
// do work
}
------- vcwidget.cpp
#include <genwidget.h>
void MyWidget::modifyWidget(HWND id)
{
// find Panel widget
Panel ^widget = find_widget_from_list(id);
// do work
}
I think I have to change my design in the following way to give each environment its own interface.
------- genwidget.h
class MyWidget
{
public:
virtual void modifyWidget() = 0;
}
------- qtwidget.h
class QtMyWidget : public MyWidget
{
public:
QtMyWidget(QWidget *panel);
void modifyWidget();
}
------- vcwidget.h
class VcMyWidget : public MyWidget
{
public:
VcMyWidget(Panel ^panel);
void modifyWidget();
}
Firstly, I would avoid using std::string as a return value from the DLL. Unless you plan on using the same compiler (and version) and stl, there is a good chance that you won't be able to properly read the string.
Secondly, the problem is that QApplication::allWidgets accesses a private list. allWidgets is implemented like this:
QWidgetList QApplication::allWidgets()
{
if (QWidgetPrivate::allWidgets)
return QWidgetPrivate::allWidgets->toList();
return QWidgetList();
}
Since you are calling it from a DLL, QWidgetPrivate::allWidgets hasn't been created (You didn't create a new QApplication inside the DLL). A good explanation as to why the QApplication in your application isn't shared with the DLL is what happens to global and static variables in a shared library.
These are the problems. Since it seems that you aren't really concerned about whether you are using different versions of the libraries and different compilers, then you could just pass the QWidgetList to the DLL.
// In test.h
extern "C" TESTSHARED_EXPORT std::string DllFun(QWidgetList *list);
// In test.cpp
std::string DllFun(QWidgetList *list)
{
std::stringstream temp;
temp << "Number of widgets from DLL " << list.size();
return temp.str();
}
// mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{
typedef std::string (*FunType)();
HMODULE handle = LoadLibrary(L"Test.dll");
FunType DllFun = (FunType)GetProcAddress(handle, "DllFun");
if (Fun)
{
QWidgetList all = QApplication::allWidgets();
std::string DllWidgets = DllFun(&all);
std::string InternalWidgets = FunInternal();
ui->textEdit->append(QString(DllWidgets.c_str()));
ui->textEdit->append(QString("\n"));
ui->textEdit->append(QString(InternalWidgets.c_str()));
}
}

access wxFrame(s) & wxDialog(s) globally

I'm new to C++ and new to codelite and also new to wxCrafter. I'm trying to build some GUI apps, but I'm messed up about object passthrough in C++. I spent a few hours and I just understand a little bit of that. First, to pass variables between wxFrame/wxDialog, I should create a instance of that class.
in frameA.cpp
void frameA::buttonAClicked() {
frameB * frameB1 = new frameB(NULL);
frameB1->connect(this);
}
in frameB.cpp
void frameB::connect(frameA *upper) {
//now I can access frameA via upper
}
But for a more complex case(e.g. 10 frames), values entered by user need to be shared between frames. I think it's better to make the frames/dialogs to be handle by a parent. Since all classes were triggered by main.cpp, so I think MainApp() will be good idea. So I tried to do this:
main.cpp:
class MainApp : public wxApp {
public:
frameA * frameA1;
frameB * frameB1
//frameC, frameD, frameE etc.
MainApp() {}
virtual ~MainApp() {}
virtual bool OnInit() {
frameA1 = new frameA(NULL);
frameB1 = new frameB(NULL);
frameA1->connect(this);
frameB1->connect(this);
SetTopWindow(frameA);
return GetTopWindow()->Show();
}
};
in both frameA.cpp and frameB.cpp:
frameA::connect(wxApp *par) {
this->parent = par;
}
Now I'm able to access MainApp via parent, but the two member objects(one is itself) was not found. Am I missed something? I'm really new to C++. Is that any better way (or a formal way) to do?
There is convenient way to make kind of global data in wxWidgets application. Create file ApplicationData.h:
#pragma once // replace with #ifndef ... if not supported by your compiler
class frameA;
// place here required forward declarations
// ...
struct ApplicationData
{
frameA* frameA1;
// any other data you need
};
Include this file to application class h-file:
#include "ApplicationData.h"
class MainApp: public wxApp
{
public:
ApplicationData applicationData; // or may it private with get/set functions
...
};
Finally, you can access applicationData from any place of wxWidgets application:
ApplicationData* pData = &wxGetApp().applicationData;
// Set/read global data members here:
// pData->...
See also: wxGetApp function definition in wxWidgets reference: http://docs.wxwidgets.org/2.6/wx_appinifunctions.html Note that you must add IMPLEMENT_APP and DECLARE_APP macros to make it working.

Error passing custom Object from QtScript to C++

I have written a custom class which is available in QtScript through a Prototype. Also another global class is available which should be used to print the custom class generated in QtScript.
This is my custom class (very simple ;) ):
class Message
{
public:
int source;
int target;
};
This is the prototype I am using:
class MessagePrototype : public QObject, public QScriptable
{
Q_OBJECT
Q_PROPERTY(int source READ getSource WRITE setSource)
Q_PROPERTY(int target READ getTarget WRITE setTarget)
public:
void setSource(const int source);
int getSource() const;
void setTarget(const int target);
int getTarget() const;
};
The setter / getter are only changing / printing the corresponding Message object through a qscriptvalue_cast(QScriptable::thisObject());
Now my script looks like this:
var test = new Message;
test.source = 5;
print(test.source);
GlobalObject.sendMessage(test);
So, the script compiles fine and the print() command does what it should, it prints 5. But the problem is the sendMessage function of my GlobalObject:
void MessageAnalysis::sendMessage(Message msg)
{
qDebug() << "[Message]" << msg.source << msg.target;
}
This piece of code always prints: "[Message] 0 0".
MessageAnalysis is registered as "GlobalObject" for QtScript. Also I have registered Message and Message* as Metatypes and the constructor, prototype and everything else. This seems to work.
Does anyone knows why the values have been changed in QtScript but are not accessible from my C++ function? Or what I am doing wrong?
Ok. After several attempts I fixed it.
I changed the sendMessage function to accept a QScriptValue instead of a Message as parameter. Now I can get the properties out of it without problems.
Seems to work fine now :)

Segfaults with singletons

// Non singleton
class MyLogManager
{
void write(message) {Ogre::LogManager::getSingletonPtr()->logMessage(message);}
}
class Utils : public singleton<Utils>
{
MyLogManager *handle;
MyLogManager& getHandle { return *handle; }
};
namespace someNamespace
{
MyLogManager &Log() { return Utils::get_mutable_instance().getHandle(); }
}
int main()
{
someNamespace::Log().write("Starting game initializating...");
}
In this code I'm using boost's singleton (from serialization) and calling Ogre's log manager (it's singleton-type too).
The program fails at any trying to do something with Ogre::LogManager::getSingletonPtr() object with code
User program stopped by signal (SIGSEGV)
I checked that getSingletonPtr() returns address 0x000
But using code Utils::get_mutable_instance().getHandle().write("foo") works good in another part of program. What's wrong could be there with calling singletons?
Real version of Utils class:
class Utils : public singleton<Utils>
{
protected:
ConfigManager *configHandlePtr;
LogManager *logHandlePtr;
public:
Utils()
{
configHandlePtr = new ConfigManager();
string engineLog = configHandle().getValue<string>("engine.logFilename", "Engine.log");
logHandlePtr = new LogManager(engineLog);
}
~Utils()
{
delete configHandlePtr;
delete logHandlePtr;
}
ConfigManager &configHandle() const { return *configHandlePtr; }
LogManager &logHandle() const { return *logHandlePtr; }
};
And here is the real code of LogManager class:
class LogManager
{
protected:
string mDefaultPath;
public:
LogManager(const string &logPath = "Engine.log") :
mDefaultPath(logPath) { }
void write(const string &message, const string logFile = "")
{
string workPath = mDefaultPath;
Ogre::LogManager *logHandle = Ogre::LogManager::getSingletonPtr(); // [logHandle=0x000]
Ogre::Log *log2Handle = logHandle->getLog(workPath); // [SEGFAULT]
log2Handle->logMessage(message);
Ogre::LogManager::getSingletonPtr()->logMessage(message);
}
};
UPDATE:
I have a static library (there is my engine code) and the main own programm which links static this library. When I call my config handle (which doesn't use Ogre) everything is okay! There is also resourceManager, it uses Ogre too. And it fails like logManager. Both this managers uses Ogre's singleton. Maybe it's impossible to call it from another library?
It feels like you have typical "static initialization order fiasco" - your Utils instance created before one (or both) of other singletons.
Try change Utils::configHandle() to something like this:
ConfigManager &configHandle() const {
static std::auto_ptr<ConfigManager> configHandlePtr(0);
if (!configHandlePtr.get()) {
configHandlePtr.reset(new ConfigManager());
// init configHandlePtr like you want
}
return *configHandlePtr;
}
I don't know Boost's singleton, but I notice some strange things in your 'Utils' class.
First of all, getHandle returns a reference to handle, but handle is a local variable that goes out of scope if you leave the method, so the reference to it will also be invalid.
Second, you didn't initialize handle in the getHandle method.
Are you sure your Ogre LogManager is correctly initialized?
Or maybe with your libraries you have one instance of the singleton in each library and only the one in your main program is correctly initialized?
In this case you have to declare the singletons in your libraries as "extern" but I'm not sure it applies to statically linked libraries.