How to share global object in other modules?

Pages: 123
Do a simple basic singleton. Header only, only default value for instance in cpp file. Include this header in any module which will need access to it. Now any code can get access to same instance by calling MySingleton::instance();

That is all. No need for inheritance. No need for auxilary functions. You may have init() function if you want to initialize your singleton with command line values, but otherwise it should be simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Singleton
{
  public:
    static void init(int argc, char** argv)
    { instance_ = istance_ ? instance_ : (new Singleton(argc, argv)); }

    static Singleton* instance()
    {
        if( !instance_ ) {
            //Handle error: trying to access uninitialized Singleton
            return nullptr; //Default behavior. Might be better to throw an exception or create it in default state
        }
        return instance_;
    }
//public instance members
  private:
    static Singleton* instance_;
    Singleton(int argc, char** argv)
    {
        //implementation of your constructor
    }
    Singleton(const Singleton&) = delete; //Disallow copy
    ~Singleton() = default; //So nobody but us could delete it
//Private instance members
};
MiiNiPaa
Thanks. So you basicly say that I should remake the MyGlobalClass (or G) to Singleton.

So here:
//implementation of your constructor
Should start my program, like creating instances of my modules, parsing arguments from command line, etc right?
It was you who told that your singleton needs access to command line arguments, so you should know better.
That depends on what your class should do and what it should not. Maybe it should parse command line arguments, maybe it is main() job and then you just should pass extracted parameters to instance. Everything depends on your program design.
I cannot make it working.

Singleton.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class S
{
  public: // public instance members
    static void init(int argc, char** argv);
    static S* I();
	
	char * workingPath;
	int actualSource;
	int actualTarget;
	bool getRegEx;
	bool readDirectoryFiles;
	int IsDirectory(std::string path);

	CLParser CLPars;
	FILE_ File;
        std::vector<SRC> sources;
	std::vector<TARGET> destination;

  private: // private instance members
    static S* instance_;
    S(int argc, char** argv); // implementation of constructor
	S::S(const S&);
	S::~S();
};


Singleton.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "stdafx.h"
void S::init(int argc, char** argv)
    { instance_ = instance_ ? instance_ : (new S(argc, argv)); }

S* S::I()
    {
        if( !instance_ ) {
            //Handle error: trying to access uninitialized S
            return nullptr; //Default behavior. Might be better to throw an exception or create it in default state
        }
        return instance_;
    }
S::S(int argc, char** argv)
    {
    }

// errors C2761: '{ctor}' : member function redeclaration not allowed:
S::S(const S&) = delete; //Disallow copy
S::~S() = default; //So nobody but us could delete it
};


parse3.cpp
1
2
3
4
int main(int argc, char* argv[])
{
	S::init(argc, argv);
}


Errors:

singleton.cpp(30): error C2761: '{ctor}' : member function redeclaration not allowed
singleton.cpp(30): fatal error C1903: unable to recover from previous error(s); stopping compilation
Last edited on
MiiNiPaa wrote:
Do a simple basic singleton. Header only, only default value for instance in cpp file. Include this header in any module which will need access to it.
If you wish, you can move parametrized constructor away, but leave functions without definition in their rigtful place.
The = delete and = default within class definition are C++11 syntax. If you don't have compiler support for them, then you cannot use them. If you don't know what they mean, then read the C++11 documentation.
And you forgot only thing where you actually need cpp file: define default value for instance.

S* S::instance_ = nullptr;

And you have extra }; after destructor definition.
Thanks, the program works. I am yet interested how to to this for Meyers singleton. In Meyers singleton the instance_ is local variable in the static function instance() which should be returned. It can be initiated on first run and then it can be returned. But if it is static, it cannot be shared between S::init(int argc, char** argv) and S::instance(). So how could it work? I guess that S::getInstance(int argc, char** argv) would be wrong.
Just a quick example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MS
{
    static void init(int argc, char** argv)
    {
        if(!param) {
            param = new Params;
            param->argc = argc;
            param->argv = argv;
        }
    }
    MS& instance()
    {
        static MS inst;
        return inst;
    }
private:
    MS()
    {
        if(!param)
            throw std::logic_error("Not initialised");
        //Use MS::param to access constructor parameters
        //rest of constructor
        delete param;
    }
    MS(const MS&) = delete;
    ~MS() = default;
    struct Params
    {
        int argc;
        char** argv;
    };
    static Params* param;
    //rest of private members
};

MS::Params* MS::param = nullptr;
There were 2 small errors. Missing public and static keywords.

I am now having linker errors with redefinition of the S::param and one unsolved external.

CLParser.obj : error LNK2019: unresolved external symbol "private: __thiscall S::~S(void)" (??1S@@AAE@XZ) referenced in function "void __cdecl `public: static class I & __cdecl S::I(void)'::`2'::`dynamic atexit destructor for 'inst''(void)" (??__Finst@?1??I@S@@SAAAV1@XZ@YAXXZ)
file.obj : error LNK2001: unresolved external symbol "private: __thiscall S::~S(void)" (??1S@@AAE@XZ)

Using V++03:

1
2
S(const S&); // = delete;
~S(); // = default; 



Also why you don't separate it into two files to separate functions implementation?
It was just for the sake of example.

You can move definitions of default constructor, init and instance functions to the implementation file. also definition of MS::param should be in the inplementation file.

= delete and = default plays critical role here and should not be removed like that.
I am back checking the Gang of four pattern (S) and now I noticed that the instance_ is not initiated.

1
2
void S::init(int argc, char** argv)
    { instance_ = instance_ ? instance_ : (new S(argc, argv)); }

I passed the line with new, and the instance_ is value 0.

Also why the Meyers singleton (MS) does not have the new keyword? I cannot find how the instance can be created. I run the MS::init(argc, argv)) to initiate. Should I then use copy constructor to create new instance of S?

In your code
1
2
3
4
5
    MS& instance()
    {
        static MS inst;
        return inst;
    }

inst is not mentioned anywhere else, so it is not set to instance. So should I add the lines likethis to the MS& instance()?
 
 inst = inst ? inst_ : (new S(argc, argv));
the instance_ is not initiated.
You forgot the line S::instance_ = nullptr; in your implementation file.
Do not skip any lines in example. They all are needed for proper functon.

I cannot find how the instance can be created.
What do you think static MS inst;does?
It is works exactly like int x; or string y; or MyCoolClass z;. And static keyword makes it be shared between calls.
Last edited on
In the singleton Gang of Four I have:
S* S::instance_ = nullptr;
exactly as you have written. I believe this is correct otherwise it would return error.

I am not sure how should the Ctor and Dtor look in C++03:
1
2
3
4
S::S(const S&){
delete instance_;
};// = delete; //Disallow copy
S::~S(){};

does not work.

Is this the problem?

1
2
3
4
S::S(int argc, char** argv)
    {
	CLPars.ParseArgs(argc, argv);
    }


Edit:
Should the constructor be empty? When the new is called then the constructor is called and after the constructor is returned, then the new should return the pointer to instance, right? Is so, that the constructor should be empty.
ParseArgs will try to access S::I() however the instance is not yet created. So it will break.
Last edited on
Should the constructor be empty?
No, it should construct your object completely from scratch before any calls to instance() are made.

So your argument parsing shold take place in constructor and do not all instance();

Why did you deleting instance in copy constructor? You should make sure that it is not called in first place.
So you can do something like
1
2
3
S::S(const S&){
assert(false && "Calling copy constructor!");
};// = delete; //Disallow copy 
When I call:
instance_ = instance_ ? instance_ : (new S(argc, argv));

... new S(argc, argv), valid pointer is returned. However the result is instance_ = 0;
Last edited on
try instance_ = (instance_ ? instance_ : (new S(argc, argv)));. And compile with max warning level. It should warn you about this.
I was wrong. It is not 0 but nullptr ... so the address is 0x00000000 and I thought it is the same as 0, but then I expanded and I see there is object. So the assignment
instance_ = ... causes break of the program. So it breaks on return from the new.
Last edited on
nullptr and 0 are same. It is null pointer.

I told that youi probably need parenteses. Or you have error in your constructor.
1
2
3
4
S::S(int argc, char** argv)
    {
	assert(false && "Calling copy constructor!");
    }

here it stops with report R6010 has been called
Pages: 123