🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++ tutorials

Started by
93 comments, last by taby 1 year, 8 months ago
struct Functor
{
	Functor(int valueToFind) : m_value(ValueToFind) {}
	bool operator()(const int value) { return value == m_value; }
	int m_value;
}

int main()
{
	Functor f(5);
	std::vector<int> list = {0,1,2,3,5,4,8,6};
	
	std::find_if(list.begin(), list.end(), f); //but this is no diffenrent than passing captured lambda here.
	return 0;
}
//And it dues to this
template< class InputIt, class UnaryPredicate >

InputIt find_if( InputIt first, InputIt last,
                 UnaryPredicate p );

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Advertisement

@joej I was wondering if these tutorials cover your interest in the four pillars of object-oriented programming?

https://www.codingninjas.com/codestudio/library/four-pillars-of-oops

https://www.geeksforgeeks.org/object-oriented-programming-in-cpp/

What is the point of templates with lambdas?

taby said:

What is the point of templates with lambdas?

Encourages the compiler to inline the lambda inside the template function being called. Without that, you either pass a function pointer and eat the indirection cost or if your lambda is capturing, then your argument is a std::function (or custom implementation that does the same thing) and you possibly incur a heap allocation. I say “possibly”, because if your capture is small enough and your compiler vendor (or library vendor, if you're not using std::function) implements small-block optimizations then the capture may be stored in the same allocation, along the pointer.

JoeJ said:

NightCreature83 said:
The reason your solution with the template is faster is because of how std::function works and not because of lambda/functor.

Thanks. Actually i have confused functor with std::function.
I must admit i don't know what ‘functor’ means at all, thus my terminology is quite lacking ; )

A “functor” in C++ land is shorthand for a “function object." Lambdas produce functors with an anonymous type, essentially auto-generating a class that overloads operator().

I am given to understand that it means something quite different in functional programming and category theory, however.

Thanks for the input, guys!

taby said:
I was wondering if these tutorials cover your interest in the four pillars of object-oriented programming?

To me, it's just wishful thinking. Good intent, but it does not (always) work, causing more problems and obstacles than it aims to solve. I see now way to do it right in general. The concepts remain useful only in cases.
People just disagree on this. But some arguments:

The goal to group data and functions in objects conflicts with the higher goal of designing data in a way it can be accessed most efficiently.

Abstraction assumes the author and users agree on what can / should be hidden. It relies on assumptions of the author, which may not hold after demands on application change. No problem if the author and the user are the same person, otherwise we likely get discussions about concepts instead a solution. Imo, there should be a way to access private data and functions, so a workaround becomes at least a possible option.

In practice OOP makes it more difficult to track who modifies state. As complexity grows, shortcuts become needed, conflicting with initial design goals and introducing hacks. Once we need to break rules for good reasons, the rules become an obstacle and consequently turn into confusion. So it turns out better to have no rules at all.

The pillars stand for high design goals, but in practice you then need a caterpillar to deal with them >; )

I think the big thing in C++ is the OOP, in the sense that it provides an alternative framework of thinking about and grouping code. That is imho THE big thing if you're coming from C where everything is a function (not sure what javascript is doing, I don't know the language). Starting from the 4 pillars is likley a good idea so there are design principles available.

Already inheritance has a few pitfalls that beginners fall into, there is a the composition versus inheritance problem, for example.

The second big thing are the standard containers that simply exist out of the box. The unique_ptr and shared_ptr classes makes you explicitly think about ownership, which in C is all implicit (although most likely your student is already doing it even if he/she doesn't realize it).

I am not very much at home in what each C++ version provides, but this looks like c++11 to me, mostly.

This may be enough for the next 6 months to experiment /get experience with.

Beyond that, you run into the zillions of features of newer C++ versions to shorten code and give a (possibly much) bigger punch in a line of code. Lots of nice stuff, but until you run into the problem where it's useful, it's hard to understand why it exists at all.

A different (and independent) branch is performance, eg write code that exploits the CPU features, such as sequential access to data, etc. Basically saying (I think): OOP is nice, but in the 20% of the code where performance counts, you should think not only in OOP but also in C.

Finally, in your list, you may want to skip what exists in C as well (argc- argv, mersenne, binary files, file systems, multi-threading at first sight), or just compress it, or point to “read this to learn how to X in C++-speak”. Multi-threading is maybe too complicated if your student never did that, but then it's “multi-threading in general” rather than "how to do it in C++".

Alberth said:
I think the big thing in C++ is the OOP

C++ may have objects and we may spend a lot of time talking about things that we call objects, but I don't really consider C++ an OO language when you factor in its dominant idioms, especially the more modern ones. C++ may have started out as “C with classes," but the language has evolved a lot since the ‘80s to include elements from other paradigms, especially from functional programming (see: lambdas, abstract algorithms, ranges) and generic programming. It may be the case that quite a few programmers whose style hasn't evolved much in the last two decades still treat C++ as “C with classes” - incorrectly, I claim - but I think a better way to put it is that C++ is “C with a more expressive type system and a cultural preference for zero-runtime cost abstractions."

I suggest that if you're reading a resource that is teaching you that OOP is C++'s big thing, then that resource is at least a decade out of date. Possibly even older, at least in mindset - the “prefer free functions” advice dates back to the mid '90s!

JoeJ said:
Abstraction assumes the author and users agree on what can / should be hidden. It relies on assumptions of the author, which may not hold after demands on application change. No problem if the author and the user are the same person, otherwise we likely get discussions about concepts instead a solution. Imo, there should be a way to access private data and functions, so a workaround becomes at least a possible option.

The purpose of abstraction (in the OO sense) is not just to hide things for the sake of making the code easier to read, it's also to:

  • ensure that a design contract is upheld
  • introduce a seam beyond which change of some sort can occur without the user needing to modify their own code to preserve correctness, possibly without even requiring a recompile

Giving access to private data makes both of these more difficult, because it then relies on the discipline of the user not to use it and create dependencies on the implementation. Some programmers would abuse that mechanism just because they can, or because it solves their immediate problem quickly, creating problems later. I'm not confident such a mechanism would be worth the misapplications that would result.

JoeJ said:
As complexity grows, shortcuts become needed, conflicting with initial design goals and introducing hacks. Once we need to break rules for good reasons, the rules become an obstacle and consequently turn into confusion. So it turns out better to have no rules at all.

Modifying your code for changing requirements should always be an option. Encapsulation mechanisms can certainly be applied in ways that make change difficult, but I would prefer to refactor rather than break a type's design contract. If encapsulation needs to be broken in one place, then the type's design contract is changing and we do our readers (including future-us) a disservice by not changing the design contract to accommodate its actual use. Don't hack in shortcuts, canonize them, change the code to make them unnecessary! We're programmers, modifying code is what we do!

Adding a new accessor to a class should not be so difficult that your first impulse is to break encapsulation. If that IS frequently your first impulse, I suggest it would be worth examining your work environment to understand why. I know this is a gamedev site, so perhaps I already know the answer to this, but just how much pressure are you typically under that you cannot take the time to do even basic changes to a type's interface and what has disempowered you from pushing back on that pressure?

Or is the issue that you want private access to library code that you don't maintain? That would mean making your code vulnerable to changes to the library's implementation that don't affect its interface, which sounds to me like a recipe for quite a bit of pain…

JoeJ said:
The pillars stand for high design goals, but in practice you then need a caterpillar to deal with them >; )

In what way?

Oberon_Command said:
C++ may have objects and we may spend a lot of time talking about things that we call objects, but I don't really consider C++ an OO language when you factor in its dominant idioms, especially the more modern ones. C++ may have started out as “C with classes,

Wanted to quote the whole paragraph, but that's a nice definition i can befriend with, being so critical about OOP. : )

Oberon_Command said:
ensure that a design contract is upheld

introduce a seam beyond which change of some sort can occur without the user needing to modify their own code to preserve correctness, possibly without even requiring a recompile

Yes, but my claim is those are high goals which can't be enforced to work just by following OO paradigms, SOLID, or whatever.
There always are failures, and if so, the enforcement becomes an obstacle.
So i'd like to break the private constraint, in cases i am willing to accepting the consequences, which include making my hacks hard to maintain and them being eventually just temporary solutions.
I mean, C was meant to do anything. We can cast one type to another, even if surely incompatible. We can shoot our own foot if we desire.
Why the hell should private / public be an exception from this tradition? This is like saying, suddenly modern programmers must be protected from themselves, because they tend to do everything wrong and are just stupid.
Well, i could add ‘#define private public’ to my preprocessor defintions. But there should be a better way for such criminals, hackers, and fools to do their thing. Just declare it bad practice, and i'm fine with that.

Oberon_Command said:
but I would prefer to refactor rather than break a type's design contract.

Exactly, but you can not always convince the author to do so.
So there should be a non recommended option you do it on your own, without a need to change the original code somebody else is responsible for.
I respect the decision of the author, and also his designs. But sometimes a need a backdoor to apply little hacks. And the evil language tells me i can't.

Oberon_Command said:
In what way?

I have a list of small changes i need to apply to the authors code each time i do an update. They pile up. It's annoying.

Well, maybe i'm currently too focused on a special case.
I'll try to make my OOP critique more general…

Imagine a node based shader editor GUI. We make this, and proudly present this to our users. We claim it's flexible and spurs creativity.
And the users like it. They drag nodes around, try to connect them with spaghetti noodles, and they get what they want. Plus some interesting, unexpected results.
Everybody is happy.

So that's good. Why? Although we use spaghetti to glue things together in arbitrary ways, it's fine. So it is sort of spaghetti code in programming terms, but it works.
Why does it work? Because the whole system represents a graph, which is what we want.

But OOP does not give us that. We get no flexible graphs - we get restrictive and cumbersome hierarchies and trees.
That's not what we want at all imo. And it will never work to hold its promise.

OOP fans will now argue i do not understand OOP. Of course it can form graphs. Our shader editor was even coded with OOP!
And you may be right. I may fail to understand it. But i just don't buy it.

I tried putting the lambda template code, but it tells me that template is not expected outside of the global scope. is there a simple sample code?

This topic is closed to new replies.

Advertisement