So what would be the correct approach in the example case, with the struct where a new member may be added? Where would the error be "reported" to, and in what way? More importantly, how would we know "whatever" was uninitialized, when its value as uninitialized may be undefined?
fooContext.bla = <smth>; // old code that didn't know about the new "whatever" feature
// member whatever could have an undefined value, so how does this check that and "report" it, fail gracefully, etc.?
Foo *foo(new Foo(fooContext));
As like he says, it seems you need a constructor of some kind in fooContext to prevent this. Even if not one to force the programmer to pass a parameter, then at least a default one to initialize the variables so their values are all defined (perhaps so as to indicate special uninitialized states that Foo can then check for).
The ideal thing to do would be to treat the context as a volatile object and check for null on .bla before attempting to use it.
And before someone gets their panties in a twist about nullptr checking, I could say the same thing would actually work with a constructor because it is perfectly possible to pass a null pointer to a constructor expecting a pointer to an object, either way it would crash. You could also pass a valid pointer but then the object pointed to may be in an invalid state, so of course you would want to check for that as well.
That was my point about not trusting outside data, because if it can fail it probably will at some point. As for how to report it that sort of depends on the context of the object, if it is imperative to the program running or whatever. Personally I would log it and throw an error if it is a serious programmer error or just log it and skip over if it is something that may have been user caused.
because it will put the object in a "ready to do stuff" state and will clearly express dependency, stopping you from doing nasty circular dependencies.
But what if you don't necessarily need that dependency for all functions? Why force someone to pass it if they don't need it for the functionality?
For example, your way I can do:
Bang, you have coupled 2 classes, created a non clear construction dependency.. multiply this by hundred of thousands of lines and you'll see the mess you are putting yourself into.
Honestly I find your answer here kind of contrived because you're making an assumption that you have two identical objects asking for a pointer to the same type. That's already a weird situation and even taken out of that situation you could create the same circular dependency by using three objects with constructors, using a constructor as a prevention to passing circular dependencies is odd at best. I'd hazard to say the object design is a bit of a problem if you end up in a situation like that in the first place.
But if you have a more real world example, I'd love to hear it.
And let's talk about validity check.. if I have a class A that depends on B.. I write a constructor:
I am stating a FACT, that A cannot possibly work without B PLUS that B will have to outlive A.
Well no you're actually stating a requirement that a reference to an object be passed in and are assuming that object will stay live longer than your object intends to use it, but that of course could be false.
Ex in some engines, a texture cannot be loaded without a reference to a graphics device. Plus I move the problem of having a VALID B to the caller.. if he has a nullptr he'll have to deference it IN HIS CODE, he will get the crash IN HIS CODE. I won't have him walking to my desk saying the game crashes in my code. I have made my intentions totally clear.
The thing is what you're suggesting really isn't fundamentally any different from having an object passed in later and checking it for validity before using it, it would have the same consequence of passing it in the constructor. The only real point you're presenting with a constructor like that is that, yes, you expect the user to know that the object cannot be created without a reference to B.
But really thats just a matter of style and varies by object, what if B was something that could change and didn't neccesarily need to be a reference? Would you pass a pointer in? What makes that information clear to the caller that they could change it later? You'd have to provide a set function of course at which case ambiguity starts to set in about how "safe" that object being passed in to the constructor really is. Which is why my method of doing it removes ambiguity, it makes an assumption that an object will not do anything upon creation and will only fail if asked to perform a task without the required dependency being set and available at that time. In your choice of object, sure, just passing in one thing could be more clear. But that will vary highly by object and have no continuity between them.
I dont have to pollute my class A code of "if (b)" checks.. basically you are saying your code is always checking for every possible member value... if you design your classes properly, you just don't need to do that.
I'm not sure where that assumption comes from, you're implying that your objects always rely on passed in references to dependences at construction time and are making a total assumption that those objects are always valid. If you accepted a pointer at any point in a constructor you would have to do the same null check for the object itself. In terms of using the object that was passed in, the code is exactly the same and you can't rely on values being valid, that code can't really change by passing a reference vs something else.
If anything you would save a little code on doing nullptr checks when using pointers. You're also making a rule that your classes can never change their dependencies without being destroyed and recreated, which is something I find debatable as a good rule.
By having a default constructed class you are creating a custom initialisation scheme different for EVERY class... some might need to have method X called after Y called after Z to init properly.. some might need Z Y X.. you're just forcing the user of the class to get into your code and understand how it works.. that's not cool man.
That's not true at all, in fact I'm not sure how you would possibly order how setting properties is called on an object, the only assumption my method makes is that calling certain methods will fail, likely logging some sort of error message if a dependency is not present when it needs to be. In most cases I would encourage the user of my code to use the overloaded constructors that fill the dependencies and only set them manually after construction if they have reason to change them or delay construction.
A good example would be a game enemy, perhaps you want to construct it and set information about it like it's position and stats or other information. But how do you set all that information at construction time? Use a factory object? Of course you could but in reality you're doing the same method as I suggest and shoehorning it into a factory interface, so the thing isn't really as important as much as the method of construction here. Also what if you want to add the entity to a world after creation? Or perhaps change a dependency of it and spawn it again in the world without destroying it. In all of these cases really, you couldn't do them if you assume the object is fully completed and "alive" at construction time by passing dependencies.
By following your methodology you're making a point that if an object requires a reference then it cannot be changed after construction, otherwise you lose the positives you even mentioned.
Or probably you are not working in C++ and missed the last 15 years of C++ evolution.. I don't know.. but I am happy to disagree with you deeply and enforce proper construction and destruction in my team.
Okay, except I am working in C++ and tend to work in C++. I'm not sure where the smart-ass attitude comes from just because someone has a different strategy for creating objects than you do, I see plenty of flaws in both methods, mine simply attempts to create consistency among how objects can be expected to behave.
--I'll cut off here until I get more replies, this reply is already extremely long between two different people to reply to!