By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,950 Members | 1,032 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,950 IT Pros & Developers. It's quick & easy.

Re-usable singleton class that can construct pointers to objects with non-trivial constructors

P: n/a
As the topic says, I wanted to make a re-usable singleton class that
could create pointers to objects with non-trivial constructors. I came
up with this:

#ifndef SINGLETON_HPP
#define SINGLETON_HPP

template<typename T>
struct DefaultCreatorFunctor
{
T * operator()() const { return new T; }
};

template<typename T>
struct ValueCreatorFunctor
{
ValueCreatorFunctor(const T& val) : val_(val) {}

T * operator()() const { return new T(val_); }

private:
T val_;
};

/* Users can add more functors for more elaborate types. */

template <typename T1, typename T2>
class Singleton
{
public:
static T1 * get_instance(const T2& creator)
{
if (!obj)
{
obj = creator();
}

return obj;
}

private:
Singleton() {}

static T1 *obj;
};

template <typename T1, typename T2>
T1 * Singleton<T1, T2>::obj = 0;

#endif /* #ifndef SINGLETON_HPP */

As you can see I've provided to default functor, one for default-
constructing and one for objects with a constructor that takes a
single value.

I've made the following test program:

#include <iostream>
#include <string>

#include "singleton.hpp"

using namespace std;

int
main()
{
int *n = Singleton<int, ValueCreatorFunctor<int>
>::get_instance(ValueCreatorFunctor<int>(4711));
cout << *n << endl;

delete n;

string *s = Singleton<string, DefaultCreatorFunctor<string>
>::get_instance(DefaultCreatorFunctor<string>()) ;
cout << *s << endl;

delete s;

return 0;
}
it seems to work but somehow I don't feel very satisfied. The syntax
for obtaining an instance is not elegant and if you want to a more
elaborate class to be handled as a singleton, you will have to write
your own functor (basically a ValueCreatorFunctor with additional
parameters). It was however a good exercise in templates and functors
for me and for that I'm glad.

Also I was thinking about ways in which I could make the singleton
delete its pointer when the program exits, so the user doesn't have to
worry about that (and risk double deletes).

As always, I would like comments from you. :-)

- Eric

Aug 14 '07 #1
Share this Question
Share on Google+
2 Replies


P: n/a
On 14 Srp, 19:44, Eric Lilja <mindcoo...@gmail.comwrote:
As the topic says, I wanted to make a re-usable singleton class that
could create pointers to objects with non-trivial constructors. I came
up with this:

#ifndef SINGLETON_HPP
#define SINGLETON_HPP

template<typename T>
struct DefaultCreatorFunctor
{
T * operator()() const { return new T; }

};

template<typename T>
struct ValueCreatorFunctor
{
ValueCreatorFunctor(const T& val) : val_(val) {}

T * operator()() const { return new T(val_); }

private:
T val_;

};

/* Users can add more functors for more elaborate types. */

template <typename T1, typename T2>
class Singleton
{
public:
static T1 * get_instance(const T2& creator)
{
if (!obj)
{
obj = creator();
}

return obj;
}

private:
Singleton() {}

static T1 *obj;

};

template <typename T1, typename T2>
T1 * Singleton<T1, T2>::obj = 0;

#endif /* #ifndef SINGLETON_HPP */

As you can see I've provided to default functor, one for default-
constructing and one for objects with a constructor that takes a
single value.

I've made the following test program:

#include <iostream>
#include <string>

#include "singleton.hpp"

using namespace std;

int
main()
{
int *n = Singleton<int, ValueCreatorFunctor<int>
::get_instance(ValueCreatorFunctor<int>(4711));

cout << *n << endl;

delete n;

string *s = Singleton<string, DefaultCreatorFunctor<string>
::get_instance(DefaultCreatorFunctor<string>());

cout << *s << endl;

delete s;

return 0;

}

it seems to work but somehow I don't feel very satisfied. The syntax
for obtaining an instance is not elegant and if you want to a more
elaborate class to be handled as a singleton, you will have to write
your own functor (basically a ValueCreatorFunctor with additional
parameters). It was however a good exercise in templates and functors
for me and for that I'm glad.

Also I was thinking about ways in which I could make the singleton
delete its pointer when the program exits, so the user doesn't have to
worry about that (and risk double deletes).

As always, I would like comments from you. :-)

- Eric
Hi.

I have few comments:
1. Method get_instance should be named create_instance, because it
always creates new instance.
2. To delete singleton object at the end of program, look at the
atexit function in <cstdlib>
3. If you need variable parameters, you can give yourself a limit,
let's say 10 parameters as maximum. Then you can write such function
this way:

template<typename T>
class SomeClass
{
public:
// .. Some stuff ...

T* Func() { return new T(); }

template<P1 p1>
T* Func(P1 p1) { return new T(p1); }

template<typename P1 p1, typename P2 p2>
T* Func(P1 p1, P2 p2) { return new T(p1, p2); }

template<typename P1 p1, typename P2 p2, typename P3 p3>
T* Func(P1 p1, P2 p2, P3 p3) { return new T(p1, p2, p3); }

// etc.
};

Only used member functions will be generated, so you do not have to
worry about size. If you will try function with wrong parameters,
you'll get compilation error.

Aug 14 '07 #2

P: n/a
On 14 Aug, 23:04, Ondra Holub <ondra.ho...@post.czwrote:
On 14 Srp, 19:44, Eric Lilja <mindcoo...@gmail.comwrote:
As the topic says, I wanted to make a re-usable singleton class that
could create pointers to objects with non-trivial constructors. I came
up with this:
#ifndef SINGLETON_HPP
#define SINGLETON_HPP
template<typename T>
struct DefaultCreatorFunctor
{
T * operator()() const { return new T; }
};
template<typename T>
struct ValueCreatorFunctor
{
ValueCreatorFunctor(const T& val) : val_(val) {}
T * operator()() const { return new T(val_); }
private:
T val_;
};
/* Users can add more functors for more elaborate types. */
template <typename T1, typename T2>
class Singleton
{
public:
static T1 * get_instance(const T2& creator)
{
if (!obj)
{
obj = creator();
}
return obj;
}
private:
Singleton() {}
static T1 *obj;
};
template <typename T1, typename T2>
T1 * Singleton<T1, T2>::obj = 0;
#endif /* #ifndef SINGLETON_HPP */
As you can see I've provided to default functor, one for default-
constructing and one for objects with a constructor that takes a
single value.
I've made the following test program:
#include <iostream>
#include <string>
#include "singleton.hpp"
using namespace std;
int
main()
{
int *n = Singleton<int, ValueCreatorFunctor<int>
>::get_instance(ValueCreatorFunctor<int>(4711));
cout << *n << endl;
delete n;
string *s = Singleton<string, DefaultCreatorFunctor<string>
>::get_instance(DefaultCreatorFunctor<string>()) ;
cout << *s << endl;
delete s;
return 0;
}
it seems to work but somehow I don't feel very satisfied. The syntax
for obtaining an instance is not elegant and if you want to a more
elaborate class to be handled as a singleton, you will have to write
your own functor (basically a ValueCreatorFunctor with additional
parameters). It was however a good exercise in templates and functors
for me and for that I'm glad.
Also I was thinking about ways in which I could make the singleton
delete its pointer when the program exits, so the user doesn't have to
worry about that (and risk double deletes).
As always, I would like comments from you. :-)
- Eric

Hi.

I have few comments:
1. Method get_instance should be named create_instance, because it
always creates new instance.
No it doesn't, it only creates one one obj == 0.
2. To delete singleton object at the end of program, look at the
atexit function in <cstdlib>
Sounds like a c-ism to me, what about a smart pointer? It's time I
started to use them.
3. If you need variable parameters, you can give yourself a limit,
let's say 10 parameters as maximum. Then you can write such function
this way:

template<typename T>
class SomeClass
{
public:
// .. Some stuff ...

T* Func() { return new T(); }

template<P1 p1>
T* Func(P1 p1) { return new T(p1); }

template<typename P1 p1, typename P2 p2>
T* Func(P1 p1, P2 p2) { return new T(p1, p2); }

template<typename P1 p1, typename P2 p2, typename P3 p3>
T* Func(P1 p1, P2 p2, P3 p3) { return new T(p1, p2, p3); }

// etc.

};

Only used member functions will be generated, so you do not have to
worry about size. If you will try function with wrong parameters,
you'll get compilation error.
Interesting approach.

Aug 14 '07 #3

This discussion thread is closed

Replies have been disabled for this discussion.