Link to home
Start Free TrialLog in
Avatar of Ingo Förster
Ingo Förster

asked on

General message class in trouble with const function

In my QT app I have a global class to show error messages.  This is working well in common functions.

int32 MdiChildHex::startHex(QString strBurnDrive)
{
    int32 ret = ::SetBurnDevice(strBurnDrive.at(0).toLatin1());
    if(showDiskbutlerMessage(ret, this)==false) {
        thisSuccessfullCreated = false;
        return;
    }
}

Open in new window


But if the function will hold a "const" in it, the function is not recognized-

int32 MdiChildHex::ReadSector(int32 sectorIndex) const
{
    char *buf = new char[2352];
    QByteArray mdata;

    int32 ret = ::ReadSectors(BS_CURRENT_DEVICE, sectorIndex, 1, BS_IMG_ISO, buf, bufferSize);
    showDiskbutlerMessage(ret, this);

    QByteArray byteArray = QByteArray::fromRawData(buf, bufferSize);

    hexView->clear();

    hexView->setData(new QHexView::DataStorageArray(byteArray));

    return ret;
}

Open in new window


The message I see in QTCreator is:
mdichild_hex.cpp:116:5: error: no matching function for call to 'showDiskbutlerMessage'
messanger.h:7:6: note: candidate function not viable: no known conversion from 'const MdiChildHex *' to 'QWidget *' for 2nd argument

message.h
#ifndef MESSANGER_H
#define MESSANGER_H

#include <QtWidgets>
#include "FoxSDKExport.h"

bool showDiskbutlerMessage(int32 nError, QWidget* parent);

#endif // MESSANGER_H

Open in new window


message.cpp
#include "messanger.h"
#include "utils_common.h"
#include <QMessageBox>


bool showDiskbutlerMessage(int32 nError, QWidget* parent){

    if (nError != BS_SDK_ERROR_NO){

        TCHAR chError[2048];
        int32 nLength = 2048;

        ::GetText(nError,chError, &nLength);

        QString errDesc;
#if defined (WIN32)
        errDesc= QString::fromUtf16(chError);
#else
        errDesc= QString::fromUtf8(chError);
#endif

        QMessageBox::information(getMainWindow(),  QWidget::tr("Diskbutler Error"),
                                  errDesc);
        return false;
    }

    return true;
}

Open in new window


What is the secret here with const?
Avatar of pepr
pepr

Generally, the const function must ensure that its code cannot change the member variables. This way also all called functions in the body must have interface that proves that they cannot change them. This means that you have to add const also to them.

(Monday morning, being too dumb for more detailed answer. :)
non-member functions cannot be declared as const. the simple reason is that global functions don't have private or protected member variables where they have access to. by declaring a member function as const, you would state that the function doesn't change member data (of the class object) and doesn't use non-const member functions (which might change object data). for global functions all this could not apply.

Sara
@Ingo Forster
Since you have not posted all codes here, tried writing similar code using your code at Linux.

a)
Intially used following function without const
int32 MdiChildHex::ReadSector(int32 sectorIndex)

Open in new window

Compiled successfully

b)
Tried using gdb to know the type of the class:
$ /usr/bin/g++ -g -Wall message.cpp -I. mdichild_hex.cpp
$ /usr/bin/gdb -q ./a.out
(gdb) break mdichild_hex.cpp:116
(gdb) run
Starting program: ./a.out
(gdb) ptype this
type = class MdiChildHex : public QWidget {
  public:
    int startHex(QString);
    int ReadSector(int);

Open in new window

Here this pointer referring => class MdiChildHex

c)
I commented the line // showDiskbutlerMessage(ret, this);
Recompiled for making compilation to be success. Hence commented the call to showDiskbutlerMessage
Again gdb:
(gdb) break mdichild_hex.cpp:116
(gdb) ptype this
type = const class MdiChildHex : public QWidget {

Open in new window

Here this pointer referring "const class MdiChildHex"

Hence
Replace:
showDiskbutlerMessage(ret, this)
With:
showDiskbutlerMessage(ret, (MdiChildHex*)this);

to resolve this issue.
d)
Write these changes in document to have all changes, like svn/git.

Following lines out of scope for your query:
I like Hill Jason ( a lot ) from Global Exchange Services(GXS) (who taught me a lot) (Also I shared few things which I learned) from 2005 till 2013) :)
I am writing following comment for policies at Global Exchange Services(GXS):
I do not have any code/certificate from GXS from 2013 till now.
>> taught me a lot
those information were written in my brain not at disk :)
I was thinking before sleeping now:
1. Design (architecture) document may inform the development team to have function being const and no need to modify the object when that class being const.
In this case do not use following replacement:
Replace:
showDiskbutlerMessage(ret, this)
With:
showDiskbutlerMessage(ret, (MdiChildHex*)this);

Follow other way:
Write one more overloaded function using const QWidget* and handle const related actions inside that function.
bool showDiskbutlerMessage(int32 nError, const QWidget* parent)
handling const QWidget* pointer.
That depends on your architecture(including operating system level changes => CYGWIN_NT/AIX/Linux/UNIX/HP-UX/SunOS including related bit (32/64/Kernel model/node name/...)

$ export PS2='COMPLETE_COMMAND $ '
$ echo "AIX 5.4/5.3/4.1/4.2/4.3/...
COMPLETE_COMMAND $ CYGWIN_NT x86_64/x86/...
COMPLETE_COMMAND $ HP-UX 11.00/11.11/11.22/11.23/...
COMPLETE_COMMAND $ Linux x86/x86_64/...
COMPLETE_COMMAND $ SunOS SPARC x86/x86-64/...
COMPLETE_COMMAND $ UNIX i686/x86/x86_64/..." | /usr/bin/sort -u
AIX 5.4/5.3/5.2/4.1/4.2/4.3/...
CYGWIN_NT x86_64/x86/...
HP-UX 11.00/11.11/11.22/11.23/...
Linux x86/x86_64/...
SunOS SPARC x86/x86-64/...
UNIX i686/x86/x86_64/...

Open in new window

I have to ask since no one else did. Why do you have a 2nd arg parent, since it is not used in your function, showDiskbutlerMessage?
bool showDiskbutlerMessage(int32 nError, QWidget* parent){

Open in new window

Your problem goes away if you remove it.
murugesandins experiments refer to the type of this; and this is a good experimental approach. I will try to explain the importance of the  type of this to explain away the mystery of const.

Your code shows that ReadSector is a const method in the class MdiChildHex. This means as others have stated, that the compiler will flag obvious attempts to modify the MdiChildHex instance as an error. Somewhere, in your code, you created an instance of MdiChildHex. This instance allows you to call this const ReadSector method. For a const method, the compiler will treat this object as a const object. This const object has, as you probably know, a self pointer to the object; and it is called this. So, the compiler treats the type of this as a pointer to a const object to prevent modification to the const object; and the type in this method that is pointed to by this is const MdiChildHex, because, otherwise, the showDiskbutlerMessage method would be allowed to modify the MdiChildHex instance.
int32 MdiChildHex::ReadSector(int32 sectorIndex) const
{
...
    showDiskbutlerMessage(ret, this);

Open in new window

The signature of the 2nd argument, this, is therefore, 'const MdiChildHex *' (also, as clued in by your error message). this is a pointer to your const instance (i.e., object). So now, the compiler is trying to find a showDiskbutlerMessage method where the 2nd arg is a const object pointer being passed in.

The 2nd arg type of the global function, showDiskbutlerMessage, is a pointer to a type, but that type is not a const.
bool showDiskbutlerMessage(int32 nError, QWidget* parent){

Open in new window

And this signature permits this global function to modify the object, which the calling method guarantees not to happen. The fact that the global function currently is not even using the 2nd arg, and therefore not modifying the parent object does not establish a guarantee never to modify the object. (Later, a maintainer could modify the function and try to modify the object.) The compiler is catching a discrepancy between the guarantee (i.e., the contractual nature of the ReadSector method's api), and the actual showDiskbutlerMessage usage.
Here is a little program for you to run in your debugger. Without looking at the comments, try to figure out which version of the global functions,  showDiskbutlerMessage, will be called.
class Base
{
public:
	virtual int _val() const = 0;
};

int showDiskbutlerMessage(int nError, Base* parent);

class foo : public Base
{
public:
	foo(int v) : Base(), val(v) {}
	virtual int _val() const { return val; }
	void test1();
	void test2() const;
private:
	int val;
};

// Notice that these two showDiskbutlerMessage functions have
// different guarantees by virtue of the type of the 2nd arg.
int showDiskbutlerMessage(int nError, Base* parent) {

	return parent->_val();
}

int showDiskbutlerMessage(int nError, const Base* parent) {

	return parent->_val();
}


void foo::test1()
{
	const foo local_foo(22);
	int val = 0;
	val = showDiskbutlerMessage(1, &local_foo); // &local_foo is Type const foo*
	val = showDiskbutlerMessage(1, this);  // this is Type foo*
	this->val = 11;
}

void foo::test2() const
{
	const foo* ptr_local_foo = new foo(122);
	int val = 0;
	val = showDiskbutlerMessage(1, ptr_local_foo);  // ptr_local_foo is Type const foo*
	val = showDiskbutlerMessage(1, this);  // this is Type const foo*
}

int main()
{
	foo foo1(5);
	foo1.test1();
	foo1.test2();
}

Open in new window

@phoffric
>> experimental approach
Thank you for your comment :)
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.