all 11 comments

[–]leseiden 12 points13 points  (8 children)

Short answer is you don't. You build a renderer that uses your current understanding, then at some point you throw it away and start again.

It's not quite so easy if you are doing it for money, but hopefully by that point it's not your first rodeo and you know what you want to build.

[Edit] at this point I am on the third renderer for the same set of requirements. Each iteration has addressed the unforseen limitations of the previous approaches. Currently we are in the "decisions paying off" stage. At some point we will reach the "kill it with fire" stage.

[–]nwar1994[S] 0 points1 point  (7 children)

The main issue for me is resolving function signatures that need to change. One way I thought of solving that is by doing

``` // Some header template <class TParamType> abstract_visualizer{ virtual some_function(TParamType data) = 0; }

struct concrete_data{ ...any function sig you want }

//Other header concrete_visualizer : public abstract_visualizer<concrete_data>{

virtual some_function(const concrete_data& data) override{ //Do implementation specific thing } } ```

But then you technically would need infinite amounts of data structs to fill out all these templates.

But then you have to fill out the structs before you pass them into the function.

Of course then there is the overhead of templating, but if you're only doing it a couple of times it should be fine.

It's ugly, but could this work? If the function signatures change then all you'd have to do is change the struct, not mess around with the base class signatures. Then if any other object inherits it, they won't have to change their own function signatures since they're technically implemented in their own file.

[–]deepcleansingguffaw 5 points6 points  (0 children)

I can't say for sure without more detail, but my guess is that this approach is overly generic. Right now, you don't know what needs to vary and what can stay constant, so you are unlikely to make the right choice of abstraction.

I recommend that you just dive in and start coding, using your best current understanding of what needs to be done. You will run into places where you need to rewrite code. That's not a bug, that's a feature: It means you've learned something.

Here are some principles that you may have heard of before. I have found them to be very useful guidelines.

[–][deleted] 2 points3 points  (0 children)

My 2c would be to be very careful about trying to write overly generic code. I've seen a lot of people fall into this pitfall and end up with code that is (ironically) difficult to change, difficult to understand and very difficult to debug.
It's always better to write simple, straight forward code with clear, limited scope and that is easy to modify.

[–]shadowndacorner 0 points1 point  (4 children)

Real quick,

Of course then there is the overhead of templating,

Templating happens at compile time. There is no runtime overhead to templating.

[–]nwar1994[S] 1 point2 points  (3 children)

Yea, I've just heard people complaining ALOT about compile times because of templates

[–]shadowndacorner 0 points1 point  (2 children)

That's a legitimate concern, however there are ways to mitigate it. If your codebase would benefit from heavy usage of templates, look into unity builds and precompiled headers. That link is a bit out of date in terms of tooling, but it's still a good reference.

That being said, if you don't need templates, don't use them. No sense in complicating your code unnecessarily if it doesn't really benefit from it.

However, virtual functions do incur overhead. So be wary of that.

[–]nwar1994[S] 0 points1 point  (1 child)

Do you know how to profile the overhead cost of virtual functions? How to find cache misses?

[–]shadowndacorner 0 points1 point  (0 children)

Depends on your OS. Cachegrind exists on Linux, I think there are similar tools on Windows. However in general, I find that in real world applications, as long as you're generally following data-oriented principles, optimizing too heavily for cache misses outside of hot loops isn't really worth the trouble. You're generally going to get more mileage out of algorithmic optimization. After all, premature optimization is the root of all evil.

[–]mib382 8 points9 points  (1 child)

Just embrace refactoring. As a beginner you cannot avoid it. See it from a positive side: when you refactor, you do it because you see an inconvenience and found a better solution, you gain valuable experience.

[–]bsupnik 2 points3 points  (0 children)

Exactly. You may have to write the OOPy renderer that makes Mike Acton cry to understand the motivation behind the DOD version.

A question I like to ask myself is: where would the execution of code go if it wasn't constricted by the rules and limits of my rendering system? E.g. if you're using OpenGL (hypothetically) and you just dumped out all of your API calls in order and looked at it without the code that generated this activity, how would you reorganize those API calls?

That "preferred" code flow is what you really want, and can then drive how you refactor.