-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.

  • 1
    related (possibly a duplicate): [Alternatives to the singleton pattern](http://programmers.stackexchange.com/q/147698/31260) and [What alternatives to a singleton are there for a class which only can have one instance?](http://programmers.stackexchange.com/questions/302901/what-alternatives-to-a-singleton-are-there-for-a-class-which-only-can-have-one-i) – gnat Aug 11 '16 at 18:54
  • Calling the question "Which of these singletorn alternatives is the best approach for a C++ game server" would fit better ? I did not find the answer I was looking for in none of the existing singletorn related questions around stackoverflow. – Raúl Sanpedro Aug 11 '16 at 19:12
  • 7
    Um.. Don't use a Singleton? *[ducks]* – Robert Harvey Aug 11 '16 at 19:40
  • Haven't done C++ in a while... but with this design, in a multi-threaded app, the destructor for instance will be called when the application exits, even if some thread other than the main thread is still using it. – gnasher729 Aug 11 '16 at 21:28
  • 1
    @gnasher729: If the main thread in C++ exits, the program terminates. Even if other threads are still active. – Nicol Bolas Aug 11 '16 at 22:08
  • How about: use a namespace instead of a class for things that are global? – user253751 Aug 11 '16 at 23:21
  • @immibis: that probably warrants a question by itself, usual reasons include wanting a closed construct instead of an open one, wanting something which can be used as template parameter. – AProgrammer Aug 12 '16 at 01:02
  • 1
    Be very careful about doing any non-trivial work (such as accessing the network) in the destructor of a static object. C++ makes very few guarantees about the order in which such "static destructors" run, which means that by the time ~TConnectionManager() is called, your program is in an unspecified state, and any cross-module call you make can blow up in your face. It's usually better to write something like static auto* instance = new TConnectionManager(); so that the destructor never gets called (and the OS will take care of closing the network connections when the process exits). – Etienne Dechamps Aug 13 '16 at 14:50
  • @NicolBolas: True. The problem is twofold: Your main thread calls destructors, then does things like atexit (), then abruptly shuts down the program and all threads. A different thread could be running singleton code, and in the middle of that the destructor of the singleton is called, and then things go wrong. Say the singleton was modifying a file and now the file is half written and your thread crashes because the singleton was destructed. Personally, I think singletons should never go away until the whole application stops running. The second problem, which is independent of – gnasher729 Sep 11 '16 at 14:02
  • singletons really, is that threads will be abruptly interrupted at a random point when the application exits. While the application probably exits at a well defined point, a thread could be in the middle of an operation that MUST be finished. @EtienneDechamps: To some degree you can overcome this by having another static object, whose destructor "shuts down" the singleton in a safe(r) way, without destructing it. The "shut down" code might finish any critical operations and refuse to start new ones. – gnasher729 Sep 11 '16 at 14:03
  • @RobertHarvey There are things like Config (cross-cutting concerns) where singleton or global variable is the easiest path to go if the config is used by every class in your application and you have 100+ classes, imagine how many hours you've lost just by adding it to every constructor and using DI... The same thing applies to logging. – Konrad Dec 23 '19 at 15:57

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
  • 198,589
  • 55
  • 464
  • 673
blackpen
  • 191
  • 5
  • So I would handle variables in the same way ? Using the extern keyword and such ? – Raúl Sanpedro Aug 12 '16 at 01:27
  • If you want to save yourself of typing a long-named-global-function or a long-named-global-variable, you can do so by wrapping it up in a namespace and later use it with "using namespace". If you are instead trying to short-cut typing access to a long named public-static variable of a class, you can do so, by writing a forwarding function and wrapping it up in a namespace and later use it with "using namespace". Does that work for you? – blackpen Aug 12 '16 at 17:28
-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
  • 16,695
  • 3
  • 33
  • 81