Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. [SOLVED] Use class without explicitly instantiating it
Forum Updated to NodeBB v4.3 + New Features

[SOLVED] Use class without explicitly instantiating it

Scheduled Pinned Locked Moved C++ Gurus
9 Posts 3 Posters 4.6k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T3STYT Offline
    T3STYT Offline
    T3STY
    wrote on last edited by T3STY
    #1

    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).

    1 Reply Last reply
    0
    • JKSHJ Offline
      JKSHJ Offline
      JKSH
      Moderators
      wrote on last edited by
      #2

      http://www.learncpp.com/cpp-tutorial/812-static-member-functions/

      Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

      1 Reply Last reply
      0
      • T3STYT Offline
        T3STYT Offline
        T3STY
        wrote on last edited by T3STY
        #3

        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.

        1 Reply Last reply
        0
        • Chris KawaC Offline
          Chris KawaC Offline
          Chris Kawa
          Lifetime Qt Champion
          wrote on last edited by Chris Kawa
          #4

          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();
             }
          1 Reply Last reply
          1
          • JKSHJ Offline
            JKSHJ Offline
            JKSH
            Moderators
            wrote on last edited by JKSH
            #5

            @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.

            Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

            1 Reply Last reply
            1
            • T3STYT Offline
              T3STYT Offline
              T3STY
              wrote on last edited by
              #6

              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.

              JKSHJ 1 Reply Last reply
              0
              • Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by Chris Kawa
                #7

                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.

                1 Reply Last reply
                1
                • T3STYT T3STY

                  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.

                  JKSHJ Offline
                  JKSHJ Offline
                  JKSH
                  Moderators
                  wrote on last edited by
                  #8

                  @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.

                  Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                  1 Reply Last reply
                  1
                  • T3STYT Offline
                    T3STYT Offline
                    T3STY
                    wrote on last edited by
                    #9

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

                    1 Reply Last reply
                    0

                    • Login

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • Users
                    • Groups
                    • Search
                    • Get Qt Extensions
                    • Unsolved