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

Discussion in 'C++' started by Eric Lilja, Aug 14, 2007.

  1. Eric Lilja

    Eric Lilja Guest

    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>::eek:bj = 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
     
    Eric Lilja, Aug 14, 2007
    #1
    1. Advertising

  2. Eric Lilja

    Ondra Holub Guest

    On 14 Srp, 19:44, Eric Lilja <> wrote:
    > 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>::eek:bj = 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.
     
    Ondra Holub, Aug 14, 2007
    #2
    1. Advertising

  3. Eric Lilja

    Eric Lilja Guest

    On 14 Aug, 23:04, Ondra Holub <> wrote:
    > On 14 Srp, 19:44, Eric Lilja <> wrote:
    >
    >
    >
    > > 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>::eek:bj = 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.
     
    Eric Lilja, Aug 14, 2007
    #3
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. William Payne
    Replies:
    5
    Views:
    478
    William Payne
    Oct 12, 2004
  2. baibaichen

    trivial or non-trivial object

    baibaichen, Jan 12, 2006, in forum: C++
    Replies:
    3
    Views:
    941
    osmium
    Jan 12, 2006
  3. Peng Yu
    Replies:
    5
    Views:
    407
    Juha Nieminen
    Sep 19, 2008
  4. Mukesh
    Replies:
    4
    Views:
    640
    Paul N
    Mar 26, 2010
  5. Jon Egil Stand
    Replies:
    2
    Views:
    108
    Jon Egil Stand
    Mar 8, 2007
Loading...

Share This Page