A new feature i want in my C++: Contexts
Take your example to clear things up; when you call foo.setAlloc() it looks like you are performing an operation on the class but you aren't, you are doing something to a local variable which isn't even named in the expression.
Right away you have muddied the interface ("Does this function do something to foo itself? or to a context class?") and remove the clarity of what dependencies each function has ("I've created a context, I've set 4 things, I call two functions... which functions depend on what things from the context?") and, based on the reading, you need to provide ALL the details for the classes dependencies up front before you can call any functions (You no longer know what the functions are using, therefore in order to be safe all dependencies, regardless of the function you are calling them needing them are now required to be resolved.)
So, if you create an instance of a class Foo on the stack which requires a Context consisting of 4 things before you can call any function on foo you have to ensure the whole classes context dependencies are completed because you have no way of knowing what dependencies the function requires as they are now hidden away from you.
The argument of 'what if you need to pass all your loggers and managers to every single function' fails the 'what if' test right away - you are attempting to fix a 'problem' which only exists in badly designed code. In every practical situation I've come across the parameters can be either provided at construction of an object or a very limited subset of external systems are required at any given point.
At work we have a gigantic program that relies on an object (let's call its class "Environment", although that's not its actual name). Everything gets passed a pointer to an Environment, and from the Environment you can get access to all sorts of data. There is a global Environment that is used as default in many places if you don't specify one explicitly.
The resulting code is a stinking pile of dung. Everything depends on everything else and it would be impossible to have more than one object of type Environment, because the global one is used in too many places. The program is so large that nobody understands what all parts do, and refactoring this would require rethinking every part of the code, so nobody does it.
In my opinion this type of Environment object is an antipattern, closely related to the big ball of mud. It is much better to make the dependencies explicit by passing pointers or references to all the objects needed, even if it might seem tedious at times.
The other alternative is using the above mentioned environment class that gets passed everywhere, and you need many of those dpending on subsystem. Passing individual context thingiea like loggers and all separately wouldnt b nice when you decide to add a new one and have to change every single method in the program - instead of just a single CommonContext class that adds a logger or something to be passed.
The other alternative is using the above mentioned environment class that gets passed everywhere, and you need many of those dpending on subsystem. Passing individual context thingiea like loggers and all separately wouldnt b nice when you decide to add a new one and have to change every single method in the program - instead of just a single CommonContext class that adds a logger or something to be passed.
That's the kind of thinking that will get you in trouble. If you decide to add a new object in your program, you need to think about what parts of the program need to know about it and pass it where appropriate. If you add it to the CommonContext, you effectively have no design to your program and every part of the program can access every other part. This makes it hard to think about individual parts, prove that they are correct or test them. It also makes it impossible to reuse some part of your program in another program.
Another architecture that might be worth thinking about is signals and slots. This allows each component to not know much about the others. They simply send signals that can be picked up by other components and they have slots where a signal can be plugged in, so when the signal is sent they do something in response. The program initially instantiates all the objects and plugs signals into slots. I have used this in a couple of projects and I like it quite a bit.
I was thinking the CommonContext class for things which are practically globals, allocators and loggers (not much else comes to mind :c)
This might be too complex in its current form to be actually worth it.
I still think it would work for the above mentioned uses, seeing as you can always split the commoncontext class to have different dependencies for different subsystems or simply provide different values for the logger or allocator (one logger for physics another for networking)
In some of the examples i gave in the first post, the physics manager acts as a kind of "RAM". Your game objects only use it for allocation, freeing and accessing. Operations like collision detection between all physical objects are done by outside code. Id like the physics manager act similiar to RAM. I dont need to pass around references to "RAM" when i want to access my objects (because RAM is a global, how evil is that D:)
So, if we made RAM less evil, so that its an object (yay, now we can theoretically have multiple RAMs and write code that is meant to run on multiple machines in a single project!) and needed to access any object, how would you like to handle it?
I would just make all my objects inherit from "RAMContext", and before creating the "Game" or whatever class in main, set the context to a RAM object. Then all my objects could simply assume the variable containing RAM to be there, and access objects like ram.this and ram.that, no need to pass RAM to every single method. (lets ignore the fact that we need to explicitly access all objects via the RAM object)
I want my C++ to not have evil globals like "RAM", it hides a major(eh?) problem with the language! ,_,
class Foo{
public:
SomeType doSomething(BoringManager1 m1, BoringManager2 m2, NiceParameter p);
}
as
class Foo{
public:
SomeType doSomething(NiceParameter p);
}
but the normal way to "hide" the managers is making them the fields of an object:
class SomethingDoer{
private:
BoringManager1 m1;
BoringManager2 m2;
public:
SomethingDoer(BoringManager1 m1, BoringManager2 m2);
SomeType doSomething(Foo f, NiceParameter p);
}
In other words, adding a SomethingDoer class to represent the "context" of doSomething() allows the client code to ignore the managers as much as possible while providing the proper means to take care of manager lifetime and associate them together.
Hah! I was thinking about the exact same thing before and also wanted to name it context. Sometimes static members are used in this manner. Object counters, common configuration data, etc. The problem ist that this static state is global for all instances of the class. Sometimes you only want to share state between a subset of all instances.
But there is no need to add a new concept to C++, you can already do that with what you have at hand. You can either create a context strcuture and pass that to the constrcutor or you can create object collections that hold shared data. I much prefer the latter, as it keeps the class focused on itself and pushes responsibility for interaction into a higher level.
+1 for just pass it in and/or dependency injection.
There's no reason for this to be part of the language. (Though C++'s lack of reflection capabilities limits your options as far as auto-magical solutions go).