Classes inside classes and Monostates, oh my!

Hello people of the interwebs. I have two questions to ask!

It seems when I'm tooling along in a project, which happens to be quite large I find myself tending to make calls that look like thus:

Renderer.m_Map.GetCell(0,1).GetPosition()

Now after a while it gets really confusing for me so I'm here to ask:

Is this something that is recommended?

Does one recommend that I put this many classes inside classes like this as often while leaving out accessors to the classes that are inside them? For example, would it be recommended to make a function in say: Render that specifically calls GetPosition in the cell that is contained in the map class? Like:

Renderer.GetPosition(0,1) <- this would do all the calls inside the function. Or does doing this violate the whole, each class does one thing?


The second question is about (I hope I get the terminology right) monostates or pure static classes. If I for say use the above example and say that m_map is a class of map and there is and will ever be only one of those in my project should I just forgo putting that in Renderer as a member and just make the map class a pure static class and include the class file in any file that will alter it?

All and all I'm just wondering what is the best way to utilize these while keeping readability above all.
Chaining of function calls is idiomatic in C++.

The rationale for modified evaluation order and sequencing rules in C++17:

Consider the following program fragment:
1
2
3
4
5
6
void f()
{
 std::string s = “but I have heard it works even if you don’t believe in it”
 s.replace(0, 4, “”).replace(s.find(“even”), 4, “only”).replace(s.find(“ don’t”), 6, “”);
 assert(s == “I have heard it works only if you believe in it”);
}

The assertion is supposed to validate the programmer’s intended result. It uses “chaining” of member function calls, a common standard practice. This code has been reviewed by C++ experts world-wide, and published (The C++ Programming Language, 4th edition.) Yet, its vulnerability to unspecified order of evaluation has been discovered only recently by a tool.

Even if you would like to blame the “excessive” chaining, remember that expressions of the form std::cout << f() << g() << h() usually result in chaining, after the overloaded operators have been resolved into function calls.

It is the source of endless headaches. Newer library facilities such as std::future<T> are also vulnerable to this problem, when considering chaining of the then() member function to specify a sequence of computation. The solution isn’t to avoid chaining. Rather, it is to fix the problem at the source: refinement of the language rules.

http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0145r1.pdf
For example, would it be recommended to make a function in say: Render that specifically calls GetPosition in the cell that is contained in the map class? Like:

Renderer.GetPosition(0,1) <- this would do all the calls inside the function. Or does doing this violate the whole, each class does one thing?
That depends. Are you also going to have Renderer.GetName(0,1), Renderer.GetAttributes(0,1), etc.? If so, then no, you should not do that.

The second question is about (I hope I get the terminology right) monostates or pure static classes. If I for say use the above example and say that m_map is a class of map and there is and will ever be only one of those in my project should I just forgo putting that in Renderer as a member and just make the map class a pure static class and include the class file in any file that will alter it?
Do you need to enforce that only a single instance ever exists?
JLBorges, maybe I misunderstand the OP's question, but I feel the use of replace(), std::cout <<operator, and functions like that are only tangentially related to Wootenstein's design question.

The OP's problem seems to be more of a design question, asking about two related subjects: (1) the advantage or disadvantages of excessive composition/"layering" of objects inside classes, and (2) whether or not the public interface should let the user skip the layering by making both the Renderer class have a public function called GetPosition() even though the Cell class also has a function called GetPosition().

I think OP is worried that he's forming his program like Matryoshka dolls, if that makes sense.

I'm not sure what the solution is. I've kinda ran into this problem myself, for example: A game has a list of rooms. A room has a list of walls. A wall has its bounding coordinates. I don't have the code with me now, but I think I mitigate this through the user of (a) helper functions and (b) using object references for any repeated chaining.

(a) example: Design your program so that there wouldn't be a reason in the first place for the user of the Renderer class to have to access the map to access each cell to access each position. Instead, the Renderer class should have a function called Draw() or something, and inside the Draw() function, you can internally access whatever you need.

(b) If you must repeatedly use "Renderer.m_Map.GetCell(x, y).GetPosition()", then make a reference to map instead.
1
2
3
4
Map& map = Renderer.m_Map;
for (int y = 0; y < 10; y++)
    for (int x = 0; x < 10; x++)
        map.GetCell(x, y).GetPosition();

Doesn't really change much, but can make it look nicer.
Last edited on
Topic archived. No new replies allowed.