Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making default constructor private in QT custom object

I'm reading this Qt doc page about custom types, and it states the following:

The default constructor, copy constructor and destructor are all required, and must be public, if the type is to be integrated into the meta-object system.

Suppose I have an object that needs to be built with some required parameters, because it has no sense to be built using the default constructor, for example:

struct IntPair
{    
    IntPair(int first, int second);
    ~IntPair();
};

To make it available in the Qt Meta Object system, as the doc states, it requires the default constructor. But practically, it has no sense to give the opportunity to build an IntPair object without a pair of integer numbers (sorry for the ugly example).

Is there a way to achieve this without implementing the default constructor? I'm thinking about a sort of friendship between my object and the Qt Meta Object system...

Basically, I cannot understand why the default constructor is needed.

like image 462
ABCplus Avatar asked Sep 15 '15 14:09

ABCplus


3 Answers

There are two parts to the question:

  1. Achieving a custom Meta Object without implementing a default ctor.
  2. Understanding why a default ctor is required by Qt in this case.

Other respondents have addressed (2) already.

I wish to address (1).

I wrote a class, and I intend for users of this class to call a ctor I wrote which requires several arguments. However, because of the Qt-related requirements, I am forced to add a zero-argument constructor.

It would make me happy to at least make the zero-arg ctor private, so that I could enforce that all user code EXCEPT moc-generated "magic" code will be barred from using that ctor.

Hello, happiness! It is possible.

You can indeed use friendship to make the default ctor private and still use Qt Metatype.

It looks something like this:

class MyClass {
  Q_GADGET

  Q_PROPERTY(QString text READ text)

 public:
  MyClass(QString text, bool sometruth, int someint);

  QString text() const { return text_; }

 private:
  // Works in my project using Qt 5.12. (see hints below if it fails for you)
  friend struct QtMetaTypePrivate::QMetaTypeFunctionHelper<MyClass, true>;
  // Prefer the ctor that takes arguments. This ctor only exists to satisfy Qt.
  MyClass();

  QString text_;
};

There are two ways you can solve the problem of figuring out WHAT to befriend.

You can mark the ctor private, try to recompile, and scrutinize the compiler error to figure out what other type is trying to access the ctor of your class.

Or, you can put an assert(false); in the body of your ctor, create a binary with debug symbols (including Qt debug symbols), then look at the stack in the debugger when the assertion fails. The stack will show the Qt-internal member-function or free function that called into your ctor. Friend whatever that caller is.

This last method (using the debugger) is what worked for me. (I wasn't fluent enough in compiler-ese to discern which type from the output of the gigantic compiler error was what I needed to add as my friend.)

like image 170
pestophagous Avatar answered Oct 20 '22 13:10

pestophagous


It has to do with QVariant's (templated) implementation.

Look at qvariant.h in the QT5.5 source code tree, and you'll find this:

       T t;
       if (v.convert(vid, &t))
            return t;
       return T();

And also:

       old->~T();
       new (old) T(t); //call the copy constructor

Hence the need for a public constructor/desctructor, and copy-constructor.


The advantage of all of this is being able to use your custom type in signals/slots (and other meta-object magic), but there are drawbacks like in your situation. It's just a trade-off that you have to live with.

As a workaround, you could have some sort of "init()" method that actually initializes the object after it's constructed. Not as safe/elegant, but it works.

like image 2
AgentLiquid Avatar answered Oct 20 '22 12:10

AgentLiquid


As to why, there's a design reason behind it. It involves a "Identity vs Value" discussion that I think is too long to paste here.

As to how, @AlexanderVX commented on using default values in arguments.

like image 1
Felipe Lema Avatar answered Oct 20 '22 13:10

Felipe Lema