(excuse any mistakes in the code - i have not actually tried this yet and because globals are evil, im probably unaware of how to use them correctly. Will try to fix any problems.)
Imagine the following case:
We have a pile of game entities, each having logic, and each often needing access to the scene object so they can interact with the surroundings.
We then have code to update all these different entities as in:
void Scene::update()
{
runMachines(*this); //Lots of moving parts - may affect surroundings
growGrass(*this); //Need to check other objects - grass may impale those who stand on it while its
growing
glowLight(*this); //Heat the neighbors
for each object do
{
for each otherobject do
{
checkCollisions(*this)
doGravity(*this)
sayHi(*this)
}
}
etc.
}
As you can see, quite a lot of Scene in there. I realize that this is not much. But if we take the hypothetical situation where the number of such objects is 4 (since we dont want to combine everything into a single scene object for example) and the logic is a lot more complicated, this gets quite redundant. In most practical situations, scene could be considered a global. We dont do that, because two scenes should be able to exist, and making it a global would remove ALL passing of Scene around.
So, what if there was a compromise such that:
-Scene is still passed around as a parameter where it does not feel like mindless repetition of a fact the code should understand (im basically looking for a statement like "using Scene" here)
-It is still obvious where scene might be passed. If its a global, we have no control over where scene can be accessed.
-It does not limit the amount of scenes to 1.
So, i was thinking of a class that sets a scope-limited global, which can be set in a scope, and then accessed from whatever methods you call from that scope as it were a global.
Implementation:
template<typename T, int identifier=0>
class Using
{
public:
Using(T* ptr) : prevImplicit(implicit), implicit(ptr)
{
}
~Using()
{
implicit=prevImplicit;
}
static T* get()
{
assert(implicit!=nullptr);
return implicit;
}
private:
static T* implicit=nullptr;
T* prevImplicit;
}
Usage:
Using the example, instead of passing Scene to each method, we set a temporary scope limited global:
Using<Scene> scene(*this);
And within the methods needing access to the scene we do:
Scene* scene = Using<Scene>::get();
and then use it to apply whatever logic needed. The scene thus does not have to be passed in as a parameter, it is now a less evil global! :DD
Now, to explain some details.
We might have a scope using Using to apply a scope limited global. But what if a method is called from this scope, which also uses Using to also set a global of type Scene?
We have two mechanisms to handle this case:
1) We store the previously set value in the temporary Using class. When we return back to the first scope from the subscope, the value of the global will be reset to what it was before. Please do note that there might be some corner cases related to threads and lambdas and those might need special approaches.
2) We have a second template parameter, identifier, which allows us to have more than one such "implicit parameter". This is more for cases where you want to use implicit parameters, but have two or more of the same type. If you are handling eg. an array of them, it does no longer make sense to try to pass them as implicit parameters. Enums can be created to have eg PRIMARY_SCENE and SECONDARY_SCENE. I cant come up with any better examples of when youd need this, but the important thing is that it is trivial to pass in multiple objects of the same type.
So, the example update method would look as follows:
void Scene::update()
{
Using<Scene> scene(this);
runMachines(); //Lots of moving parts - may affect surroundings
growGrass(); //Need to check other objects - grass may impale those who stand on it while its
growing
glowLight(); //Heat the neighbors
for each object do
{
for each otherobject do
{
checkCollisions()
doGravity()
sayHi()
}
}
etc.
}
Is there anything wrong with this approach? Please do remember that i wanted to try and find a compromise between being explicit about dependencies, while at the same time allowing this kind of gameplay code to be less strict, since it is likely to change more rapidly and such (which is why we use scripting languages for that kind of code - it does not need to be so strict)
In most cases this does not really help much, but im sure there are cases where such a construct might be useful.