0

I am new to C/C++

I tried to implement singleton-pattern.

why doesn't following code not compile. In javascript there is no problem implementing the singleton pattern in a similar fashion. (https://addyosmani.com/resources/essentialjsdesignpatterns/book/#singletonpatternjavascript)

I thought that when I declare the pointer *singleton_instance static it could be accessed by the static method. But G++ gives the error

singleton_pattern_nullptr.cpp:12:20: warning: ‘constexpr’ needed for in-class initialization of static data member ‘Grammar* Grammar::singleton_instance’ of non-integral type [-fpermissive]
   12 |    static Grammar *singleton_instance = NULL;

But even with

#include <iostream>
#include <stdio.h>    
#define NULL nullptr
    
class Grammar {         
protected:
    Grammar() {};
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;

    static Grammar *singleton_instance = NULL;          
public:         
    Grammar& getInstance();     
}; 

static Grammar& Grammar::getInstance() {
    if(singleton_instance == NULL) {
        // no star with new
        singleton_instance = new Grammar();
    }           
    return *singleton_instance;
}
    
int main(int argc, char* argv[]) {          
    Grammar *grammar;       
    grammar = Grammar::getInstance();
            
    return 0;
}

Now when I define

static constexpr Grammar *singleton_instance = NULL;

g++ spawns the error message...

singleton_pattern_nullptr.cpp:23:23: error: assignment of read-only variable ‘Grammar::singleton_instance’
   23 |    singleton_instance = new Grammar();

and still ...

singleton_pattern_nullptr.cpp:33:34: error: cannot call member function ‘Grammar& Grammar::getInstance()’ without object
   33 |   grammar = Grammar::getInstance();

I thought I could make it mutable but in this thread (static mutable member variables in C++?) I read that it shouldn't be necessary for a static member variable to be specified mutable in order to be that (mutable) because this should already be the case anyway.

So please tell me where my error is and if an implementation of a singleton like I thought of above (see javascript example) is possible in C++ or not.

Thanks

EDIT:

After having acknowledged the present answers so far, I cannot tell what I am doing wrong still. Please tell me how I can change define the pointer *singleton_instance and Grammar::getInstance() such that I can manipulate the pointer according to the idea, if possible. I now have frustranely declared both static, and declared *singleton_instance with the modifier constexpr according to the error message by g++.

It can't be that hard, can it?

Another example: https://developer-blog.net/singleton-design-pattern-in-c/

Thanks

EDIT 2:

Thanks to M47 for posting a working "Meyers Singleton" (as user idclev 463035818 annotes)

But although I set the flag -std=c++17 the compiler still responds with the warnings

**singleton_pattern.h:9:12:** warning: inline variables are only available with ‘-std=c++17’ or ‘-std=gnu++17’
    9 |     static inline Grammar *singleton_instance = nullptr;

But allow me three (two?) more comprehension questions regarding c++ please.

1 - In the header file, the method is declared as static inline

static inline Grammar *singleton_instance = nullptr;

In the definition however, you pass on these "modifiers" (?). Or is either static or both static inline implicit in the definition via the namespace/class-operator :: ? I think I have read something like that somewhere.

Grammar& Grammar::getInstance()

If this is not so, then I know there are threads on divergent modifiers in header file declarations and cpp file definitions. Could you give me a prompt?

Last but not least (minor problem) I get the warning

warning: variable ‘grammar’ set but not used [-Wunused-but-set-variable]

Thank you very much for showing me how I can get the pointer version of the singleton pattern to work ! kudos!

von spotz
  • 875
  • 7
  • 17
  • 3
    ot: why do you provide a definition for `NULL`? Looks a bit backwards – 463035818_is_not_an_ai Nov 08 '20 at 13:36
  • you are mixing several problems here, maybe better concentrate on one version of your code and the error message for that one version, the fix will be the same, no matter what wrong version you start from – 463035818_is_not_an_ai Nov 08 '20 at 13:39
  • "it could be accessed by the static method" there is no static method in your code (thats one of the problems) – 463035818_is_not_an_ai Nov 08 '20 at 13:41
  • another one is returning a reference to the singleton and trying to assign it to a pointer in the main function. you should probably learn more about C++ before going forward. it'll save you a lot of debugging time. – M47 Nov 08 '20 at 13:59
  • @ idclev 463035818 fixed that. Can't believe I didn't declare ::getInstance() static. – von spotz Nov 08 '20 at 14:23
  • @M47 I cannot point to a reference? Like in int a; int* ptr = &a; ? – von spotz Nov 08 '20 at 14:24
  • @vonspotz you can but you're not using the `&` there. it should be `Grammar *grammar = &Grammar::getInstance();` then. – M47 Nov 08 '20 at 14:29
  • @M47 Thanks for your valueable answer. I mistakenly thought this would be implicitly clear to the compiler thorugh the method definition. Thanks ! – von spotz Nov 08 '20 at 14:35

1 Answers1

3

There is no need to explicitly declare your static instance as a member variable. you could also do :

class Grammar
{
public:
    Grammar() {}
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;
    static Grammar& Grammar::getInstance() {
        static Grammar instance;
        return instance
    }
};

This way you also avoid direct use of raw pointers, potential segmentation faults and SIOFs. That being said, the singleton paradigm is not recommended according to the C++ core guidelines.

Notice that I've deleted the copy constructor and assignment in the first two lines. That's typically done in the singleton paradigm to avoid unwanted copies.

EDIT: doing it with pointers

// header file:
class Grammar {
protected:
    Grammar() {};
    Grammar(const Grammar&) = delete;
    Grammar& operator=(const Grammar&) = delete;

    static inline Grammar *singleton_instance = nullptr;   
public:
    static Grammar& getInstance();
};
// cpp file
Grammar& Grammar::getInstance() {
    if (singleton_instance == nullptr) {
        // no star with new
        singleton_instance = new Grammar();
    }
    return *singleton_instance;
}
// main.cpp
#include "grammar.h"
int main(int argc, char* argv[]) {
    Grammar *grammar;
    grammar = &Grammar::getInstance();

    return 0;
}

static inline member variables are only defined in C++17. An alternative would be to initialize the static member outside the class. It must be done in one and only one translation unit. This could potentially cause SIOFs:

// cpp file
#include "grammar.h"
Grammar* Grammar::singleton_instance = nullptr;
...
M47
  • 400
  • 2
  • 13
  • Yes, I read about deleting those. I would take this into my implementation. I also know the implementation you posted with defining static Grammar instance; in the::getInstance() method (short side-note question: why isn't that one static?) Isn't there a way with pointers and segmentation faults and SIOFs "just because I want it" ? Like in "the will of man over nature (compiler)" :D Cheers – von spotz Nov 08 '20 at 14:10
  • my bad I forgot the `static` in `getInstance`. Of course you can also do it with raw pointers as well but as you learn more about C++, you'll find restrictive tools that help you avoid bugs at the cost of overhead or more code. as idclev 463035818 pointed out, the question is not complete. I'll update my answer to include your way, but you can't will your way out of random segfaults months into a project. C++ gives you the freedom a million ways of writing things but only a handful are safe. – M47 Nov 08 '20 at 14:44
  • as much as I appreciate the core guidlines in general, imho with this item they have taken it a step too far to the subjective. In the item before they even write "Exception A global object is often better than a singleton." wihout any rationale. I mean "often better".. can it get any more vague than that? And then "Singletons are basically complicated global objects in disguise." directly contradicting "global is often better than singleton" – 463035818_is_not_an_ai Nov 08 '20 at 14:44
  • 2
    perhaps worth mentioning that this is known as the "Meyers Singleton" and that is it thread safe without additional measures – 463035818_is_not_an_ai Nov 08 '20 at 14:48
  • 1
    @idclev463035818 it is vague but I think it's more of a "don't run around with scissors thing" because there are many ways it could go wrong if you're not sure what you're doing. I currently use singletons in a project because nothing else makes sense and a "global object" would be stupid. – M47 Nov 08 '20 at 14:57
  • 1
    singletons can be a sign of poor design, but running around with scissors is something different. Once you have a singleton I don't see the danger. The danger is to overuse them. Anyhow thats a discussion for a different place. The coreguidelines do mention it and it is good that you included that in your answer. My rant was offtopic – 463035818_is_not_an_ai Nov 08 '20 at 15:25