Getting a pointer to global Input class

Started by
9 comments, last by CrazyCdn 6 years, 2 months ago

In my game engine that I'm writing I have a Engine class that connects to Allegro library and handles intermediary things like game loop, update, render and events. Specifically to handle input and it's events (from Engine class) I created Input class. This class is instantiated using a global pointer (which I know is... bad) which is passed to Engine class, so that engine could connect it with Allegro.
I wanted to create Input class to be as much independent and modular so I inherited it from IInput interface. Engine class knows only about this IInput so that in future I could switch Input class with some other class from IInput.

My question is how I could remove this global pointer of Input class? I thought of converting Input to an static class, but than I can't get a pointer to it which I need as a param to Engine class (through IInput). I also thought of making Input a singleton but I'd rather not do that.
The final goal I had in mind is to have multiple classes that are completely independent of each other and inherited of various interfaces, like IInput, IAi and ISomethingElse, so that in future I can replace Input, AI with some other implementations of same interface.

Advertisement

To remove globals is simple, just dont use globals. Pass the instances around - either manually or by automatic dependency injection.

Your game class for example is mostly likely not a static instance, but rather a instance created in your main entry point (Pointer is stored on the stack). The input class/manager should be stored into the game class and the instance passed/injected in the classes/functions which use them.

And one last tip: Forget about doing any kind of abstracting here. Do the simplest thing to get the job done, but nothing more. You can always abstract it later if you really need to.

 

But keep in mind there may be cases where you have no choice as to use a static field or a global variable - like for example a bad api without user-pointer support in a callback.

Just now, Finalspace said:

Your game class for example is mostly likely not a static instance, but rather a instance created in your main entry point (Pointer is stored on the stack). The input class/manager should be stored into the game class and the instance passed/injected in the classes/functions which use them.

Thanks, that's what I was looking for. You were right, Game/Engine class is an instance created in main entry point.

Another question, as I've been recently coding with Unity3D, I saw that they use an "global" Input class that can be called from I think almost everywhere, like it's static or global in some kind.
How come that they didn't went with the approach of passing it manually or through dependency injection to other classes? Or I misunderstood something? 

If you code in C++ you are not bound to an OOP model always. You might create "static" pseudo classes using a namespace tag and global functions. This is how most parts of my engine look like because you have two benefits; It is simple to extend such "classes" by declaring global functions in the same namespace and you are not bound to any class or static class model (that may lead to a bit better performance)


//Input.h

namespace Input
{
  ...
   /**
    Processing of input event data. If successfull results in a native handle
    identifying the device input was send from and raw data, zero otherwise.
   */
   api void* Process(void* data, InputBuffer<byte>& raw);
  ...
}

And in cpp file you could implement it and set needed instances if necessary


//Input.cpp

#include "Input.h"
  
namespace Input
{
   //define static variables here
   InputDevice defaultKeyboard = 0;
   InputDevice defaultMouse = 0;
}
  
void* Input::Process(void* data, InputBuffer<byte>& raw)
{
  ... 
}

Access is then as simple as writing


class Engine
{
  public:
     void Update()
     {
        ...
        void* device = Input::Process(...);
        if(device)
        {
           //got input
        }
        ...
     }
};

 

Wow, thanks, really interesting to code this way. I found another way too. I removed the global pointer to Input, added a pointer to itself that's inside the class, like Input::Input *_pInput and moved public methods to static ones. Now whenever I want to access this global class I just access its public static methods like Input::OnMouseMove() and this method does appropriate logic through *_pInput. I just initialize the class in main() so I can get this pointer and any outside classes don't know anything about this pointer, they just use static methods.

Honestly, I don't think there is anything wrong with using Singletons. Especially for classes you know will be accessed anywhere, it's pretty much the most straight-forward way to do it.

1 hour ago, cyberpnk said:

Especially for classes you know will be accessed anywhere

I'd challenge the notion that Input needs to accessed from everywhere. There's probably very little use in accessing Input from inside the renderer, or deep in filesystem handling code...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

1 hour ago, cyberpnk said:

Honestly, I don't think there is anything wrong with using Singletons.

Actually they tend to hide the fact that it's nothing more than a global variable, just make it a global and don't pretend it's not.  It's also not multithread safe in the least and also typically should be a warning of a bad overall design and it's time to refactor. 

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Good points. I'm definitely still learning but I find (limited) use of Singletons to be more convenient than passing pointers all over the place, though I agree it's basically a global variable.

56 minutes ago, cyberpnk said:

Good points. I'm definitely still learning but I find (limited) use of Singletons to be more convenient than passing pointers all over the place, though I agree it's basically a global variable.

There are justifiable uses for singletons, but all of them are when it's a hardware limit of some sort.  You're basically saying you will never, ever need another instance of that class or that everything and everyone can have access to it.  It makes debugging between a nightmare and near impossible comparatively.  Just passing on some knowledge.  It may seem "inconvenient" to pass around pointers (hopefully smart pointers if you're using C++; unique_ptr's .get() pointer is fine also), but object lifetime is important to know/track.  It's good to form good habits as early as possible :)

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

This topic is closed to new replies.

Advertisement