-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
  • 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
  • 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
  • 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
-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);
}
  • So I would handle variables in the same way ? Using the extern keyword and such ? – Raúl Sanpedro Aug 12 '16 at 1: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;
};

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.