[SOLVED] Use class without explicitly instantiating it



  • I have a class that creates various widgets. This class' methods should be callable from everywhere (where the header is included, obviously), but without explicitly instantiating an object of the class.
    Just like when you include the iostream library and then simply type std::cin or std::cout, I would like to include my header and use my class with a syntax like myClass::doThis().

    Can you tell me any ways that I could achieve this?

    I have read about the Singleton pattern but it doesn't really suit my needs. Also, using static data and members looked like a solution, but I'm not sure where to start and I couldn't find a guide that explains it right (actually, none that explains it, they only say "use static bla-bla-blah" without much or any code).


  • Moderators



  • I have already read lots of articles about ways of using static members of a class, including the one you linked, JKSH. However it didn't really help, I actually couldn't understand how to use static members for my purpose.
    Also, searching on the web reveals that most developers prefer instead a "singleton pattern".

    Now, I'm not sure if what I've come up with is some singleton implementation, but surely accomplishes 90% of my needs. My idea is to use a global object. Since a widget cannot be created before a QApplication has been created, a proper init() method will serve the purpose of doing the initialization. The object is created in a global namespace (or in my own namespace) and used with extern in all sources that include my object's class.
    So here's the boilerplate code I've come up with. It runs just fine and serves my purpose. However, I am looking for feedback as I might be doing something horribly wrong.
    FILE: test.h

    #ifndef TEST_H
    #define TEST_H
    class QPlainTextEdit;
    class QString;
    
    class test{
    public:
    	test();
    	~test();
    
    	void init();
    	void print(const QString &message);
    
    private:
    	QPlainTextEdit * pEdit;
    };
    
    #endif // TEST_H
    

    FILE: test.cpp

    #include "test.h"
    #include <QPlainTextEdit>
    #include <QString>
    
    test::test(){ // nothing to do in ctor right now }
    
    test::~test(){
    	delete pEdit;
    	pEdit = nullptr;
    }
    
    void test::print(const QString &message){
    	pEdit->appendPlainText(message);
    }
    
    void test::init(){
    	if (pEdit == nullptr) // if pEdit was not created, create it
    		pEdit = new QPlainTextEdit;
    
    	pEdit->show(); // and show the widget
    }
    
    /*** this is the global object  that it's meant to be used anywhere ***/
    test testWidget;
    

    FILE: main.cpp

    #include "test.h"
    #include <QApplication>
    
    // this is the global object created in test.cpp
    extern test testWidget;
    
    int main(int argc, char *argv[]){
    	QApplication app(argc, argv);
    
    	testWidget.init(); // initialize the object
    	testWidget.print("hello world!");
    
    	return app.exec();
    }
    

    As I was posting I noticed that the public ctor will allow other objects of this class to be instantiated, but I'm not quite sure how to instantiate that object if it has a private ctor (the compiler says it is private - and it's right). I need some advice on this as well.


  • Moderators

    First - if at all, you should put the extern declaration in the class header. This way you won't have to type it everywhere, just include the header.

    Second - you should not have the extern or a publicly accessible object at all. The singleton pattern goes something like this:

        //header
        class MySingleton {
        public:
            static MySingleton* instance();
        private:
            std::unique_ptr<Stuff> data;
        };
        
        //cpp
        MySingleton* MySingleton::instance {
            static MySingleton singletonObject; //it's ok to call private constructor here
            if(!singletonObject.data)
                singletonObject.data = ... //initialize it somehow
            return &singletonObject;
        }
    
        //usage
        int main(int argc, char *argv[]) {
           QApplication app(argc, argv);
    
           MySingleton::instance() -> .... //use it, just not before app is created
    
           return app.exec();
       }

  • Moderators

    @T3STY said:

    I have a class that creates various widgets.

    When I first read this, I thought you were after the factory pattern:

    // myfactory.h
    class MyFactory {
    public:
        static QWidget* createWidget(int type) {
            switch(type) {
            case 0: return new QMainWindow;
            case 1: return new QDialog;
            default: return new QWidget;
            }
        }
    }
    
    // main.cpp
    int main(int argc, char *argv[]){
        QApplication app(argc, argv);
    
        QWidget* w1 = MyFactory::createWidget(0);
        QWidget* w0 = MyFactory::createWidget(1);
        w0->show();
        w1->show();
    
        return app.exec();
    }
    

    @T3STY said:

    searching on the web reveals that most developers prefer instead a "singleton pattern".

    ...

    As I was posting I noticed that the public ctor will allow other objects of this class to be instantiated, but I'm not quite sure how to instantiate that object if it has a private ctor

    The pattern you should choose depends on what you're trying to achieve. Notice that a singleton requires lots of boilerplate code. For your class, is it worth the effort?

    If you only want to print messages to a central widget, then I think it's overkill to create a singleton. You can achieve same notation by using functions in a namespace, instead of functions in a class:

    // printer.h
    #include <QString>
    namespace Printer {
        void print(const QString& message);
    }
    
    // printer.cpp
    #include "printer.h"
    #include <QPlainTextEdit>
    
    static QPlainTextEdit* pEdit = nullptr; // NOTE: This "static" means "private"
    
    void Printer::print(const QString& message)
    {
        if (!pEdit) {
            pEdit = new QPlainTextEdit;
            pEdit->show();
            // NOTE: When the QApplication is destroyed, it automatically destroys all widgets too
        }
        pEdit->appendPlainText(message);
    }
    
    // main.cpp
    #include <QApplication>
    #include "printer.h"
    
    int main(int argc, char *argv[]) {
        QApplication app(argc, argv);
        Printer::print("Hello world!");
        return app.exec();
    }
    

    That's much fewer lines of code.

    The "static" might be confusing -- In my example, it means that only printer.cpp is allowed to access pEdit. It is completely different to "static" applied to class members. (That's one of the flaws of C++: too many different meanings for "static")

    @T3STY said:

    It runs just fine and serves my purpose. However, I am looking for feedback as I might be doing something horribly wrong.

    I don't see anything wrong with it, aside from the unnecessary "extern" that @Chris-Kawa pointed out, and the public constructor that you already pointed out. Those aren't fatal flaws though.



  • JKSH, you just made my day!
    I was coming up with a similar solution to yours, but I didn't know how to make a global object to be accessible through some functions only. So static when used within a file (or namespace) will actually make that <static> object a local private object. I could never guess it since I always thought of static applied to class/function members.
    No, I wasn't looking for the factory pattern, even though I might need that for some other purpose ;)
    I think I mislead you by using the word creates instead of... contains (?). Unfortunately, my English is not good enough at moments.
    The singleton pattern however was clearly overkill when it comes to code (for example, a singleton class is hardly extensible through subclassing). I also read it is anything but multithread safe (even though I couldn't understand why - I'll make sure I read some more about it).

    Thanks Chris for your reply as well!
    As final question, are both JKSH's solution and mine multithread safe?
    I mean, I don't care if in a multithread scenario the cronological order is not respected when printing (I read it is a common problem). I just care of having the messages printed without a print from one thread to break the print of another and loose any messages.


  • Moderators

    There are two aspects of thread safety here.

    First - creation of the singleton/global object. From the given solutions only the one I presented is thread safe and only starting from c++11. This is because in c++11 function local static variables are guaranteed to be constructed in a thread safe manner and initialized only once (a.k.a. magic statics). Unfortunately compiler support is still spotty (e.g. Visual Studio supports it starting with 2015).

    Second - accessing the singleton/global functions. None of the suggested approaches are thread safe. What's more even guarding it with a mutex is not enough as you can't access/create widgets from worker threads.


  • Moderators

    @T3STY said:

    So static when used within a file (or namespace) will actually make that <static> object a local private object. I could never guess it since I always thought of static applied to class/function members.

    Not namespaces. Files only.

    Without "static", other files can access the widget by writing extern QPlainTextEdit* pEdit. With "static", only printer.cpp can access it. Namespaces are not relevant.

    This usage of "static" comes from the C language, which doesn't have classes.

    I think I mislead you by using the word creates instead of... contains (?). Unfortunately, my English is not good enough at moments.

    Not a problem :) I can understand you quite well most of the time.

    I also read it is anything but multithread safe (even though I couldn't understand why - I'll make sure I read some more about it).

    A singleton class can be made thread-safe the same way you make any other class thread-safe -- using mutexes, for example.

    As final question, are both JKSH's solution and mine multithread safe?

    Currently. they are not thread-safe because you can only construct QPlainTextEdit and call its functions in the GUI thread. (If you violate these rules, your program will crash) To make them thread-safe, you need to do 2 things:

    1. Make sure you call an init() function from the GUI thread, to construct the QPlainTextEdit.
    2. Replace the function calls with queued invocations:
    // Replace this...
    pEdit->appendPlainText(message);
    
    // ...with this:
    QMetaObject::invokeMethod(pEdit,
    		"appendPlainText",
    		Qt::QueuedConnection,
    		Q_ARG(QString, message));
    

    This makes the function run in the thread that pEdit lives in. However, invokeMethod() only works if the function is a slot, or if it is marked with Q_INVOKABLE.



  • Multithreading is something I'll be looking deep into later. For now my problem is solved!
    Thank you both very much, guys!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.