-2

I'm now stuck in the server I am programming, simply because I do not seem to find a good and realiable way to handle my "single object" classes, I've attempted the Singleton pattern but it is just plain ugly to have to type everything over and over again.

I have this class "TConnectionManager" which basically handles all connection related code to the connections active to my server.

Singleton Pattern - connections.h

Singleton destructor would delete all connections left in "Connections" variable

class TConnectionManager
{
public:
    std::list<TConnection*> Connections;
private:
    std::mutex ConnectionsLock;
public:
    static TConnectionManager& getInstance()
    {
        static TConnectionManager instance;
        return instance;
    }

    TConnectionManager() = default;
    ~TConnectionManager();

    // non-copyable
    TConnectionManager(const TConnectionManager&) = delete;
    TConnectionManager& operator=(const TConnectionManager&) = delete;

    TConnection* CreateConnection(boost::asio::io_service& IOService);
    void ReleaseConnection(TConnection* Connection);
    void ProcessConnections();
};

For me to have to type "getInstance" over and over again is getting frustrating as it is, so I tried to search around many places and could not come up with a better solution, and there's also many different point of views.

I have attempted to do this, which seems way cleaner:

C Like - connections.h

Without a real destructor, I'd have to call the "ExitConnections" method at the end of the program to delete all remaining active connections, or set the std::atexit method

extern std::list<TConnection*> Connections;
extern std::mutex ConnectionsLock;

TConnection* CreateConnection(boost::asio::io_service& IOService);
void ReleaseConnection(TConnection* Connection);
void ProcessConnections();
void ExitConnections(); // Destructor alike function which deletes all connections

Also attempted to have a static class "namespace"

Static methods and variables (namespace) - connections.h

Same approach as for the C-Like methods and variables, use ExitConnections method act as a destructor to delete all remaining active connections

class TConnectionManager
{
public:
    static std::list<TConnection*> Connections;
private:
    static std::mutex ConnectionsLock;
public:
    static TConnection* CreateConnection(boost::asio::io_service& IOService);
    static void ReleaseConnection(TConnection* Connection);
    static void ProcessConnections();
    static void ExitConnections();
};

So I don't know which of the three ways later mentioned is the best approach to handle this situation I am stuck in.

2 Answers2

-1

Keep the singleton pattern contained in good solid design without spilling over its details to outside world.

To short-circuit typing for your particular use-case, just create wrapper functions (with names as short as you want) and put them in a namespace.

/* TConnectionManagerWrpr.h file */
#include "TConnectionManager.h"
namespace TConnectionManagerWrpr {
   TConnectionManager geC();
   TConnection* CreateC(boost::asio::io_service& IOService);
   void ReleaseC(TConnection* Connection);
   void ProcessC();
   void ExitC(); 
}

In your code, you can call them as:

#include "TConnectionManagerWrpr.h"
using namespace TConnectionManagerWrpr;

void f() {
    TConnection* tc=CreateC(...);
    ReleaseC(tc);
    ExitC(tc);
}
Robert Harvey
  • 200,592
blackpen
  • 191
-1

You can still use individual instnces of objects.

These separate objects will just be flyweight wrappers of the singleton instance. It is also a Pimpl (pointer to implementation) pattern, except that this wrapper does not even "own" the implementation object.

Note that I am not advocating this answer over any other approaches. For one, this approach requires duplicating the declaration of every method, which is a drawback that is commonly associated with decorator pattern and delegation. It is up to you to decide whether the benefit outweighs the drawback.

Note 2. You can actually put the static and the instance methods into the same class, by renaming each static method (e.g. add a "Static" prefix before each method).

Note 3. I remember having been told by a senior programmer that the getInstance() is a good thing, because it conveys the fact that all actions on this class will be handled by a singleton instance; the blatant obviousness of this wording (extra typing) helps avoid programming mistakes.

#include "TConMan.h"

class Wrapper
{
public:
    Wrapper() : m_impl(TConMan::getInstance()) {}
    ~Wrapper() {} // do nothing; real cleanup happens on TConMan

public:
    // For each method on TConMan, 
    // implement a method of same name, signature, and return values.
    // Inside each method, forward the call to the actual method 
    // on "m_impl".

private:
    // non-copyable member declarations here. 

private:
    TConMan& m_impl;
};
rwong
  • 17,140