Youtube comments of MrAbrazildo (@MrAbrazildo).
-
18
-
15
-
11
-
7:08, in old hardware, the engine instructions/data didn't fit entirely on the cache. So, depending on how many instructions an action takes, CPU had to seek the RAM, which uses to be 100x slower (maybe less in a console). On modern hardware, all instructions/data are in the cache, which has much more memory than they require, for an old game. However, RAM is still used even nowadays, for multimedia stuff: images, video, audio, textures and other more than 64 KB sized. The optimization for these large things targets to load part of the RAM on the VRAM (GPU cache memory), in a moment the user doesn't care, like a loading scene - i.e. God of War's Kratos passing through some rocks. Sometimes this is used for loading from files to RAM too.
11:58, but he is doing it for modern hardware, isn't he? The video's goal is just to explain why Quake's alg. is not meant for all cases.
13:00, the sad truth is that these pointer transformations are UB (undefined behavour). That's why the guy commented it as "evil": he just wanted to get his job done, leaving the comment for the future masochist who will deal with the potential nasty bug. UB means the operation is not standardized. So, the app may someday start crashing or giving wrong values (out of nowhere!), if any thing change from the original setup: hardware, OS, any imaginable protocol that interacts to the game. Not even old C had an expected action for that, as long as I heard.
13:52, in math, a minus exponent means that the number is divided. So, x*0.5 == x / 2 == x*2^(-1). Instead of multiplying the whole number, it's possible to change its exponent, by sum or subtraction, which are faster operations.
11
-
10
-
10
-
9
-
9
-
8
-
1:36, unit tests are important to me because:
- Test edge cases.
- Keep testing things that you think are already right, when you change code.
- In TDD, helps planing the design. This may be controversial, when 1 doesn't still know how the f() signature will be, and it's better to discover it by developing it.
- The most important: once having an incomplete f(), and a bunch of tests, the rest of development can now become really fast!
1:55, it's possible to extract that if condition from inside the f(), making another 1. Problem is that it may be seen by the rest of the project. C/C++ has a way to avoid that, while still keeping this flexibility.
8
-
8
-
4:00, to make a projectile travel alongside a 2D plane, angle is not required. It's only needed the "derived": how much Y (vertical) it'll run for each step of X (horizontal). Once having the target, its x,y will be acquired, and then comparing it to the original (your character) x,y: (Ydestiny - Yorigin) / (Xdestiny - Xorigin).
6:47, I would make it this 1st way, as a bigger map. The spawns would be coordinates related to the screen, so it would not get a problem regarding number overflow. "Node" is a dangerous word: it always feels to me that the programmer will use linked lists (slow as hell) where he shouldn't.
10:18, pitfall here: some people think that 100 will participate on the division, when it'll just multiply the result.
10:23, a bit harder? Dude, I played Van Helsing, and it was a lot harder. The harder the better. Don't be afraid to do it.
13:54, because it's hard to imagine a harder task.
7
-
7
-
7
-
6
-
6
-
6
-
6
-
5
-
5
-
3:18, you (23:54) and Casey said something that I already knew: wait the need before abstracting. This is the right way to design: focused on the practical needs. And that means building abstractions for defensive proposes too - before somebody starts to advocate FP.
3:35, 1 of the basic optimizations made by the machine is to inline f()s. The only concern is about reaching a limit, after which it won't inline or starts extra calculations about it - but 1 can set it. The actual problem about helper f()s is the options for calling them by mistake, or to feel the need to memorize them, as options, when thinking about the whole thing. To avoid this extra stress, a class to hide them can help. But this starts to require more boilerplate. The best solution is writing lambdas (within the f(), of course), if those blocks are called more than once. Otherwise, I keep them inside the f(), as explicit code, if they only make sense there.
5:03, if 2 apps have a difference of ms for each action, people will feel better with the faster 1. So, it's not just for speed-critical projects: even the common ones can benefit. Meta said that "people feel more engaged when the app is faster" (probably about ms and above).
5
-
4
-
4
-
4
-
4
-
4
-
4
-
4
-
0:03, I'll translate this: they don't use to spend time building tools. If default Rust has everything they need, ok, go for it. But don't complain later why:
- Windows 10 is 40% slower than Linux Mint 19.2, according to my tests.
- Loose too much time compiling.
- Missed out the chance to has easy-to-use tools, conciliating speed and safety.
2:16, this just means Rust has a better default behavior for safety. As long as I heard, Rust lacks the flexibility of C++. Thus, it ends up been an inferior approach, compared to C++ coded to work exactly how the user wants.
3:41, C++ does it better: it doesn't waste speed tracking object and also deletes it for you, automatically - and allows you to delete it by hand too, if you want .
4:10, this is the problem: converting C to Rust may look as a big win, but missed to opportunity to do it to C++. For instance, that means they won't ever has a switch of "verify my code now" / "now don't verify it" (for compiling speed).
4
-
0:42, if I would take care of this, I would start by rewriting this file to something much smaller. After all, 12GB is too much. Let's say names have average of 8 letters and numbers can go up to 99.9. So they are (8 + 1 (;) + 2 (2 numbers) + 1 (.) + 1 (1 number) + 1(new line char) )*8 = 14*8 = 112 bytes per line. So, writing in a binary file, 4 bits for the last fraction, 7 for the number and ~11 for a number simbolizing the name, which should be searched later in a separated table. So 4 + 7 + 11 = 22 bits, 3 bytes. If 3 is ~2,7% of 112 bytes, it means that those 12 GB would be reduced to 12x0.027 ~= 324 MB.
This would make any kind of search a lot faster.
4
-
4
-
4
-
3
-
3
-
3
-
5:13, C++ 3x1 Java (2,7x1, actually), according to a test at Dave's Garage, another ex Microsoft: https://www.youtube.com/watch?v=D3h62rgewZM&t=1342s , if you consider C# is about the same speed as Java: https://www.youtube.com/watch?v=sJC3JsvhM9o&t=1203s .
5:35, + C++ also gives you more control over the design too, allowing to make better tools, even for defensive proposes. I heard in a presentation that C++ is also used in "secure critical systems", or something like that. I'm not a bit surprised. I coded my own boundaries checking for any container, and never again had a problem about that that wasn't reported properly.
5:49, Mike Acton about this for his HPC#, an attempt to make C# the new C++: https://www.youtube.com/watch?v=u8B3j8rqYMw&t=872s .
3
-
10:15, for me this would be an annoyance only if I was making it for free. As an employee, it's ok.
16:09, I thought you would say: less code, smaller changes. Well, it seems that pair programming does that, in a speaking way.
17:25, but that would not mean that 2 individuals, working in parallel, produce in 42.6 minutes what pair programming would take 30*2 = 1 hour?
17:28, I have none of these problems.
3
-
0:00, interesting point you mentioned a startup. C++ is my favorite by far, but I don't know if I would trust it in the hands of a junior team. Maybe if there would be a long deadline ("done when it's done" mode) or if I would be watching close what they do (an almost "pair programming").
1:19, I often use inheritance, and have no issues with that. I barely can imagine its absence.
1:40, optional type is comfortable to code and understand, but it has a concerning drawback: it carries within a potential branch, which may lead to slower code. Each f() doesn't know if it's valid or not, leading to too many IFs, ugly code, redundant work and prone to slowness. It works like a 9-months pregnant woman, making everyone apprehensive.
3
-
3
-
I'll take a look at this stuff. To actually bury C++, a language should do something intrinsically different as, I don't know, change its behaviour in the middle of the code - I think COBOL used to do that. Other than that, it will be 1 more feature waiting to be incorporated to C++, making this language return to be the best, after a brief time.
2:22, the <int/char> thing is only mandatory when the compiler can't decide which type is. This example could be written as just myMax (3, 7) and myMax ('g', 'e'), because the parameters have the type in themselves.
2:35, as long as I remember, there's a compiler flag for that.
3:16, stand up comedy moment...
3
-
11:40, I use to think the general flow of data as a simple thing. But in practice, some ugly measures have to be done. Splitting classes to keep data security, more layers of data access to become clear, and so on. All things that I rather not do, but technically I feel compeling to.
People discuss how much inheritance can mess things up, but I don't use to have such issues: I start with a simple thing, focused on what I need now, and throughout time I come back to make little upgrades. It's a kind of "become forced by practice" motto.
13:15, there's a problem in multithread, called "false sharing": each time a process read a cache line (32 or 64 bytes), it blocks to all other threads the access to that line (those bytes). To solve this: put data from each thread in different cache lines.
17:20, someone said, in a recent presentation: "Classes work as black boxes: we put things there and forget about it" . I appreciate this in design: the less 1 needs to keep in mind, the easier it will be to upgrade the architecture.
3
-
3:29, C++17 was a conservative and kind small standard. C++20 changed the way we write code. It's pretty elegant, still having the old C approaches.
3:42, I don't know about Rust, but C++ keeps its compatibility with crude methods from C, so that you can still solve things in dumb way. So this already defeats the allegedly "complexity" issue. It's also utterly nonsense to talk about "stability issue" in C++. It continues having its performance and unmatched flexibility, newer features cooperating with old ones.
4:15, this is pretty stupid. OO improved safety a lot. Lambdas are often used, higher level e safer than anything C is using - and 0 cost too.
9:50, there are tools for C/C++ that catch those errors, faster than Rust would compile.
13:08, C++ has the only right OO implementation and is the most flexible language so far. Unmatched on those. Aside from that, it's capable of everything C does and implements higher level features near as comfortable as higher level languages do. So it seems more like a "master of most things".
13:14, unwise.
3
-
3
-
3
-
3
-
3
-
3
-
0:37, C has this restriction. With C++ you can also do this, as well as higher-level methods too.
0:51, C++ can use both mutable or not on the stack. The condition is if their memory size don't pass a certain amount of memory. 1:05, whenever it does, 1 can use smart-pointer, that does just what you showed here - and prevents the dangling issue.
1:19, you can pass a reference for that, as well as any other variable/object/pointer. Pointer is specially useful in situations where things can go wrong, allowing to represent the case by a null value, which is a nice idea.
Race conditions: since 2011 (C++11) we have the <atomic> library, which forbids data racing at compile-time.
1:47, finally something C++ hasn't: assigning numbers by a growing order, right at the declaration. But you can always define your custom constructor for that.
3
-
2
-
2
-
2
-
2
-
4:20, I can't even remember if I ever had a serious bug using pointers. Here go my tips for everyone, who use to have problems with that, to get rid of this issue once and for all:
- If you have to allocate memory, don't do that directly: use containers from the standard library , STL. They have their size hidden from you, and manage it automatically.
- When traversing those containers, use their own iterators (OO pointers). Member-f()s 'begin' and 'end' provide them for you. Just keep a model like this:
for_each (container.begin() + offset, container.end() - premature_end, some_algorithm);
With offset <= premature_end, and both >= 0. If you just want to run all the way (default is copy, but you can reference that, with &) :
for (auto &a_var_not_ptr: container) some_algorithm (a_var_not_ptr);
In any of these cases you deal with pointer directly.
- Reallocations may invalidate previous iterators :
std::vector <Type> container;
auto it = container.cbegin() + K;
container.push_back (M); //May invalidate.
auto x = *it; //May crash.
There are 2 main solutions for this:
a) Just "refresh it", after push_back:
auto it = container.cbegin() + K; // ≃ F5 in a webpage.
auto x = *it; //Guaranteed to work.
b) Recommended: reserve memory right after container creation:
//Chandler Carruth: "We allocate a page each time. This is astonishing fast!"
container.reserve (1024);
container.push_back (M); //Just added M, not reallocate.
auto x = *it; //Ok.
There won't has a new reallocation, as long as container.size() <= container.capacity() . This is much faster and generates a much shorter bytecode.
- If you need to allocate directly, use smart pointers instead, to do that for you on the backstage - as well as free them.
- If, for any reason, you need a C-like pointer (raw pointer), wrap it inside a class , together with a variable for its size, both hidden (no Java setter methods!) and a destructor, to automatically free the memory of a specific object, once that specific object ceases its existence.
- In this case, you will have to write a copy constructor for it, to avoid "memory stealing" , from 1 object that has copied its content, and had its destructor activated.
If you already wrote that class, and are in a hurry to use it before writing this constructor, you can still be safe by temporarily deleting its attribution operators (each_of_their_declarations = delete): if any copy is made, it will arise a compile-time error .
- I read on GCC (compiler) documentation, that the OS may not provide a pointer aiming before the container . So, if you intend to use reverse iterator, keep that in mind.
- Just like index, pointer keeps its step as large as the size of the type it's pointing to. I once had a code running on Windows and Linux, both 32 bits, using 24 bits (+ 8 bits alpha) BMP images. They were read by pointers of type 'long', the max/platform size, to keep portable for the future, automatically growing up to pixel size and OS.
When I migrated to Linux 64 bits, it started to get unstable on Linux only. It took me a couple of minutes to figure it out: pointer step became twice the intended size. Easy.
- Let's say a f() received pointer for object of class 'animal', and it accesses also the 'dog' class supposedly in it, a class that inherits animal. But this may be just a pointer to 'animal', not dog + animal.
To make this downcast, use dynamic_cast: it makes a runtime check , to see if there's "ground for pointer landing".
- Above all things, watch out for undefined behaviour . 1 of the many tricks C++ uses to get faster is to not bother the compiler, about the order it must execute things. There's operator preceding, but if that is respected, any order is accepted. So, don't do messy things like this:
pointer[index++] = ++index*5;
Sure, it will execute [], multiplication, =, those 3 in this order. But what index it will treat 1st? You must get used to have a special look for too compacted commands. Instead of that, unroll the command in the order you are thinking (read-only instructions are 100% safe, even for multithread) :
++index; pointer[index] = index*5; index++;
======= // ===========
This is about all the universe of a pointer, all tricks it might trick on you. If you keep yourself lucid about those topics, each time you deal with pointers, they will never be more-than-minutes-to-solve bugs for you.
PS: pointer is much faster than index, because it memorizes "where it is", meanwhile index goes all the way from the begin, each time it is called .
2
-
1:53, when I code, from those I prioritize, in this order:
1) Safe. And thus Encapsulated (necessary for safety), Neat and Tidy (from outside PoV), Noninvasive/Scaleable (natural for OO), Systematic (few public things). It hurts testability.
2) Performant. This 1 tends to hurt everything else.
3) Readable. After some adaptations, it uses to become Reusable/Understandable. If possible, with a deeper thought, it may also become (at least in its public interface) Simple, Elegant and maybe more Testable.
I don't know many languages, but I ensure C++ can achieve all of this. For instance, let's say a project works intensively with strings:
1) Safe: I use std::string or something alike. It's already pretty easy to use, but if I want some more automatic, I inherit it in my own custom class.
2) Performant: let's say it's hurting performance. I don't go to C's array of char. Instead, I hide in my own class what exactly the project demands.
3) Readable. After those 2 stop fighting each other, I use to make some changes towards readability/elegancy/simplicity. Right after, testability may be the target.
At the ending, it may not look as pretty as some Python-like solution, but giving all the things it's achieving at the same time, it's much better "for its proporse".
2
-
2:50, dump C++ for what? I want/need:
- Nonpolymorphic inheritance, instead of composition;
- Actual encapsulation, breakable only by a selected group of few f()s (meaning data is actually private, not indirectly public by allowing to use filters/modifiers, as C does) ;
- Full freedom to interact over a container. This way I decide the level of use constrains, by coding my own;
- Not to be forced to lose performance, because somebody else decided it for me - GC, for instance. In C++ I code things that I turn on/off security by clapping fingers. Does Rust has that? Can I turn its slow compile on/off anytime?
- Lots of things able to work hiddenly on backstage (several anytime-optional checkings, for instance), so that I can dev. powerful tools.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
9:20, it has the int8-to-platform-size_t as the precise integer. But if that doesn't need to be precise, and even more, 1 wants to make the app "future proof", a long will target the platform size, so that it'll keep being "upgraded" as platform size grows. It can even become faster throughout time, if the app was thought to make binary operations on 1 variable, if that is faster. long long then it's what it seems, twice the size, if allowed. short for the smallest or at least smaller than the middle 1, int.
12:23, cringe moment: I don't know what's this May 9th.
15:04, not all platforms allow this double the size. So something like int128_t is meant for a compiler error if not supported. long long means "the maximum possible signed size". So, if not supported, it may be shrinked back to 64 bits, since the user didn't express necessarily 128 bits.
2
-
1:07, by that do you mean "data racing" (more than 1 thread writing the same data, at the same time) ? This is easily solved since C++11, with STL <atomic> library, at compile time. The remaining issue is the "false sharing": when you have different threads changing different memories from the same cache line. So when 1 write at its portion, it "freezes" the entire cache line, not allowing the other thread to write, during that brief moment. This is a performance issue, not a bug. It's still solved by hand, having to align the data, leaving each thread to its own cache line.
1:24, what exactly Rust solves here? Those pointers are meant to acquire an opened resource, freeing it later automatically. A common C++ skill issue here is to use those pointers for data that could easily fit the caches. Since people are used to call 'new' in other languages, in C++ it'll get that memory far away, on RAM or an even worse place, becoming at least 100x slower, unless the compiler saves the junior dev.
Why C++ made life harder on that? That's because it actually made life easier: it assumes 1 wants the data on cache, thus by default it dismisses us from even having to use 'new'.
1:55, I don't know about unique_ptr. But what I know and saw, more than 1x, is that compiler is smart enough to put an entire std::vector on a cache. Assuming unique_ptr is part of it, it's prone to be free too. But of course, it depends of the memory it's holding: if it exceeds the caches sizes, it'll stay on RAM. I think there's nothing Rust can do about it.
17:12, I thought he would say that C's pointers are the same concept from Assembly. Now I'm confused, since I don't deal with it for a long time. C++ iterators do some compile time checks, while pretty much the same speed.
2
-
2
-
2
-
2
-
2:00, I disagree, because nowadays every command does many things. For instance, if you are searching something in an array, you can put a comment not the kind of "finding something", but for why are you searching that - not technically, but semantically. 2:30, here, I would write a comment about what is 'r', and why it's calculated like that. And I would do that at the right of the line, to avoid growing it downward. 3:45, I think it's not always possible: sometimes it's too intricate, and you will want to break it in parts, even harder to explain.
4:25, a majority of my f()s can be seen entirely in 1 screen. However (6:03), there are some of them that take 3 or more. And their content works as something private, that should not make sense to be exposed to the rest of the project, in a form of other f()s. Of course I'm not talking about reusable things (7:07), but about only 1 goal (7:55) that requires many steps, many initializations, many private and temporary data.
2
-
2
-
2
-
2
-
2
-
0:23, ahahhahahah! What's this, stand up comedy?! Java should be vanished for good! 3:08, which keeps being right... 3:38, a bad idea. Destructors can handle this automatically. The only tiny price is that you understand the concept of scope, the life cycle of a variable. A Microsoft employee once said in a presentation, about automatizing a task that you need to remember, once a variable cease its existence: (C++ creator) "Bjarne solved this problem, but the world keeps acting like if it wasn't" .
6:19, C++ has that too. It's called profiling (the code). Compilers have a flag for that, turned off by default.
6:35, "C++ is the language of choice for trading systems: it has high level abstractions, it's flexible, can make solid and fast tools. That's what we need" , said a trading systems employee, in a presentation. "We can do a trade faster than the light traversing upside down the tallest build!" .
8:42, maybe C#. But Java is simply awkward. They thought that the 'friend' keyword from C++ was a mistake, and removed it. Result: the setter methods expose private content. This is a shot at programmer's head! Java is anti-design!
9:55, yeah, he removed the knifes from the kitchen.
11:05, well, you can do that mess with C++: pile classes, 1 above the other, like some kind of t**, and then you go: houston.we.have.a.problem.there.is.no.decoupling()
2
-
2
-
2
-
2
-
12:49, I recently made a benchmark of unrolled loops vs normal on C++: the 1st got almost twice the speed of the conventional way. And due to macros, I made each unrolled algorithm in 2 lines (not even f()s were compiled), vs ~6-10 lines f()s for known loops. 12:55, the reason is because the unrolled code makes it clear, for the compiler, the chance to use parallelism from a special hardware for basic operations. Follow this talk: www.youtube.com/watch?v=o4-CwDo2zpg&t=2100
13:05, to C++ I use to code macros for doing things on a safer way, and then I cut them out at once, by changing 1 line and recompiling. Could Rust do the same, by activating/deactivating its safe mode, maybe using its macros?
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
7:30, I use tab = 2.
9:10, what is xPathString? You just deleted the comment. 10:15, don't delete, just put it at the right side. 11:00, mm... I wouldn't do that. 11:11, omg... I know Java is awkward, but the comment was saying where things were placed. 13:52 and 14:01, somebody put this man in jail! :)
12:25, I would permanently highlight the 'hasChildren' 1st, or if this doesn't exist in the IDE, I would use the find recurse, to spot every occurrence of it.
12:45, "equal to" and "has it" are 2 different things to me. That's why I would not delete the comment.
- Here's the proof of a DRY violation: there's the hasChildren f(), but the programmer forgot it, because the previous f() was too big.
- 13:17: at least in compiled languages, deleting (safer: turning it into a comment, actually) getXPathString() would reveal all calls to this "f()".
How C++ is elegant compared to this s**! You could write:
11:20, jsonString += Str1 + Str2 + ... StrN.
13:00, return jsonString.substr (blabla) + "]". Period!
13:40, String tagetString = shortXPath.equals("") ? "//toc" : "//".
2
-
2
-
2
-
2
-
2
-
2
-
2
-
@0ia I think that app complexity is something much more challenging than its size. It's possible to be challenged by some concept if the project demands it right away, regardless of its size. For instance, global variables. It's a known bad design choice, but if 1 only works in large projects that don't use that for decisions throughout the code (which branches it'll take), like databases, the exposure of those variables won't be felt as a dangerous thing, as it should. But coding a game for instance, even if not large, which uses to has tons of branches, taken accordingly to values from those variables, then the programmer will be thrown in a really unbearable mess.
2
-
2
-
5:30, true: if it is C++, approved; if not, we will use it by force! :)
7:50, depends on the skills of those people. Plus, the communication will has a growing challenge.
10:10, depends on how desperate is that deadline. Plus, deadline is kind of an absurd concept. About refactoring, I think is recommended to do it till certain point, when you feel things won't fall apart anymore, due to the lack of it.
12:12, for a team, I guess it's the communication. If working alone, coding skills.
15:00, true, we have C++, and it is still not able to solve things for us - it only give us the right tools.
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
2
-
3:10, this is not true. I would make a banana, gorila and jungle as 3 different/independent classes. Banana would reuse its fruit base class, as well as gorila with its animal base.
3:20, I don't have been facing this case. I guess I would just make Developer inherit Designer, and call the last 1 FullStackWorker.
10:15, I don't know, it seems that this computes only that combination flow, not all paths. Does it cover if mario gets the feather right away, for instance? Anyway, I made a C++ version that covers all combinations. After dozens lines of declarations, the user can code just 1 line. For your example:
mario << Mushroom << Flower << Feather;
assert (mario == CapeMario);
2
-
42:58, you are right, but you are comparing to C. In C++, just make_unique: it'll even dismiss you from having to write another line, as this defer (also avoiding the leak of not calling it) , because its destructor will free the mem., when the time comes (reaches the end of its scope).
43:54, wrong! C instead of C++ is. His use of macro, to help the limitations of C, was well applied. I used to use it a lot, in the past. C doesn't has defer-like, as long as I know.
2
-
2
-
0:03, coming from Ken Thompson (0:17), it's surprising why he didn't just chose C++. It has 3x the C# speed, coroutines (C++20), concurrency, and not a bad syntax (Go should be 3x sweeter to use):
0:45, auto x2 = 42;
1:19:
#include <string> //1:53.
int main () {
printf ("Hi Mom!\n"); //Or \r\n, if the system has carriage return.
std::string name = "Jeff"; //No pointer. Or auto for 'const char' pointer.
auto age = 75; auto likesGo = true;
return EXIT_SUCCESS;
}
1:58:
#include <string>
std::string myArray[] = { "emoji1", "emoji2", "emoji3" };
#include <map>
std::map <std::string, std::string> myMap = { { "robot", "robot_draw" },
{ "pixel", "pixel_draw" }, { "clow", "clow_draw" } };
for (auto x = 0; x < 10; x++) printf ("emoji\n"); //Or:
#include <iostream>
for (auto x = 0; x < 10; x++) std::cout << "emoji" << std::endl;
std::string animal = "emojiA";
printf ("%s\n", animal == "emojiA" ? "emojiB" : "emojiC");
If == is an issue for somebody, it's possible to write an alternative word/sentence:
#define is_equal_to ==
printf ("%s\n", animal is_equal_to "emojiA" ? "emojiB" : "emojiC");
auto year = 2021, *p = &year;
PS: 0:17, Ken Thompson coordinated the C project, but the active creator of the language was Dennis Richie.
2
-
1:10, Java IS very nasty code!
8:40, I didn't get it: it still has the breaks. How passed this time?
14:12, I think you make too many f()s. Some thoughts about that:
1) Whenever you extract a f(), you are putting related things a bit away from each other, and a bit near to the rest of the outside code. This also can lead to accidental calls to those f()s. /*Edit: and you'll deal with more code.*/
2) Compilers use to inline a certain amount of f()s. And if you raise that number, it can eventually get an unnecessary extra large generated byte code. This can lead to slowness.
3) 13:13, as I said on the previous video, for C++, just: return jsonString.substr (blabla) + "]" , which would be 1 line, no extra f(). But I guess Java is too bad to has that.
2
-
2
-
2:00, I don't think this is a good idea. Pointers/iterators are fast for algorithms, and I use to subtract them a lot, to get the index out of them. I don't remember having any issue about this. In C++, it's possible to write some defensive code inside f()s:
#include <iostream>
#include <string>
#include <cassert>
template <class FarPtr, class FarObj, class NearPtr, class NearObj>
auto subtract_ptrs (const FarPtr Ptr1, const FarObj &far, const NearPtr Ptr2, const NearObj &near)
{
assert (&near == &far);
assert (Ptr1 > Ptr2);
return std::distance (Ptr2, Ptr1);
}
int main () {
std::string str1 = "str1", str2 = "str2";
//std::cout << subtract_ptrs (str1.cbegin(), str1, str2.cend(), str2);//Assertion `&near == &far' failed. Pointers don't came from the same object.
//std::cout << subtract_ptrs (str2.cbegin(), str2, str2.end(), str2);//Compiling error: iterators don't match. const vs nonconst.
std::cout << subtract_ptrs (str1.cbegin(), str1, str1.cend(), str1);//Assertion `Ptr1 > Ptr2' failed. The result would be < 0.
//std::cout << subtract_ptrs (str1.cbegin(), str1, str1.c_str(), str1);//Compiling error: iterators don't match. string vs 'const char *'.
return EXIT_SUCCESS;
}
2
-
2
-
2
-
@plan.b2 Let's say you want to hide data in C, kind of a class in C++. You will have to put that data aside, in other file, to be seen only in that file, with some f()s to talk to public. And how could you inherit this code? You could repeat it with macros, but it would be awful, because of too many code. I love macros, but not for an entire class.
And let's say you want several objects of that "class" in C. There's no way, unless you transform all from that file into arrays, but the resulting code would be prone to errors, compared to a C++ array of objects.
2
-
2
-
2
-
2
-
@plan.b2 The DRY principle (Do not Repeat Yourself) protects the coder from errors. Inheritance works with it. Let's say you must compute objects and people. Both have locations, and you want compress locations coordinates to save memory, leading to not intuitive code. It's nice to have that in a class, with f()s to unpack info behind the scenes and keep a higher level dialogue. Both classes inheriting that is DRY.
Even if you use hash table, trying to say that won't inherit, because it will be an unique array/list/container, you can reach a situation in which you realize it's better to have 2 hash tables, because let's say, people is the "hot data" (more accessed), and the whole app will be faster if accessed separately. In this case, the locations class could hold an array itself, being inherited by both hash tables, keeping the DRY principle.
2
-
2
-
2
-
8:17, once I had a serious 8-days-to-catch bug (more than 1 day for me is rare), the longest in my life, as long as I remember, due to using explicit number instead of a word representing it.
10:50, from a technical point-of-view, macros could reduce this code nicely. And I would not switch a String, I would use numbers instead (global named constants), which are way faster and safer, if you consider case-sensitive and other tricky strings pitfalls. And I would not 'throw' anything, which is slow and gives nothing in trade.
2
-
2
-
2:40, this is unfair: that cartoon showed interpreter reading legal instructions, while the compiler saw errors. The actual difference is that interpreter will show to you your errors at each instruction, while compiler will list all your errors, not "leaving you on your own" - of course, compiler errors can be combined to raise new ones, but that is a minor a detail .
3:28, C++14 (since 2014) has a much better solution:
const auto foo = "hello world!"; //foo is deduced as 'const char *', but you can convert it to 'std::string' at any time.
const auto bar = 23; //It's an int, because literal number defaults to int.
const auto baz = 42F; //A float. Just trying to complicate matters.
constexpr auto fun () {
return bar + baz; //Returns float: int + float = float. Always the more detailed 1 wins. Even fun will return its value at compiling time.
}
All of that at compiling time, not losing performance, as it would happen at runtime.
2
-
2
-
2
-
2
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1:38, I used to dislike #ifdefs. Nowadays, I think they are quite nice, because they help to debug. For instance, if a block of code won't be used in some compilation, that code won't actually exist, even raising a compile error in case some piece is missing. So this is already a check, a confrontation vs what the coder is thinking. And it's possible to keep switching the switches, getting a quick statistic effect about any bug.
Codeblocks IDE can "blur" blocks not targeted to compile, a pretty nice visual effect.
4:19, I agree with you, because people use to think that Single Responsibility Principle is technically only 1 thing, but I think it may be semantically, instead of technically. So a f() may do several small/tiny technical things, to achieve 1 goal. This way, outside the f(), the rest of the project can look at that f(), thinking about that goal only. It's already an isolated functionality, despite the fact it takes more actions internally.
4:31, I completely disagree here. I already wrote tons of words in a video of his, uploaded by other channel. If someone is interested, I may write some words here too.
6:28, sorry, dude, we are waiting for Carbon. If only it changes that bad syntax...
14:35, I think this is much more important than it looks. I can't prove it, but I feel like spending more energy when travelling vertically. So this should be avoided, whenever convenient.
18:02, I personally omit { }, because I love compact code. But I wouldn't argue vs this standard. I would put them on the same line, though. 18:21, in C/C++ the if isn't ruined by comment, even without { }.
1
-
1
-
34:00, I'm not following the point here. What I heard about this happens when 2 pointers may -> to the same address. It's not the case of int and double. I'm not have been following C, but speaking for C++, this is accepted, even with the same type. The issue is that C++ might lose performance:
void f(int *x, int *y) { // They may -> to same address.
*y = 3; // It might be indirectly making *x = 3 at the same time too!
*x = *x + 2;
}
It loses performance: each time 1 of these pointers appear, it must seek the pointed address, instead of working with their value on CPU registers!
Easy portable fix: if they are not const, copy their values to local variables, and bingo: ~12x faster!
Nonportable fix: compilers have different attributes, holding/pairing those parameters.
Clang compiler has a tool to report if it's losing this performance. 1 can go to compilerexplorer.org(?), put the f() there, select Clang with -OX, and there's an option I don't remember where, which will say "variable clubbered by" something, when it couldn't optimize.
However, I think this is an issue only when compiling part of the code. Otherwise, the compiler should be able to track all the pointer steps, knowing for sure if 2 of them point to the same place.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1:50, those 2 are meant for big fat resources only. But there's a lot of memory accessing in which you can earn performance through pointers. Some days ago, I was optimizing a small project, and tried a bunch of algorithms, for passing data from string to an array. Using pointers (iterators actually) was the fastest, making the whole app 10x faster than using the slowest, sscanf() from C.
7:30, in C/C++ it's not undefined behaviour, as long as I know. It only will loose performance. There's a tool from Clang compiler that shows where/when it looses that in the code.
8:55, he said that this pointer usage was an attempt to turn around borrow checker rules.
9:25, undefined behaviour is the sum of all fears in coding, because it doesn't grant to obey rules anymore, from that point. It means that, for instance, if you test your code, it won't execute it, when you read an array, it may read after its end, and things like that. So your project will crash sometimes and work on other tries!
11:30, to take away an argument in C/C++ macros, it's needed to redefine it:
#define ptr__field (*ptr).field
But gonna need to do that for the entire class (all fields), multiplied by each different pointer name!
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I guess by now everybody understood TDD is good. You should now become more technical. Let's say I have a f() that makes several steps to reach a goal. It has several steps because they are private to it, they would not make sense outside of it - it could be prone to errors if called outside . And that f() also updates variables in other classes, because otherwise this should be scheduled to do later, and before some certain other actions take place - and would be prone to errors too, if this schedule fails to complete or, in worse cases, fails to follow a certain order too . By what I understood so far, TDD would demand that a f(), which would be written like that, be splitted in several small f()s. So, to avoid a disaster, I think 2 possible solutions:
a) Each f() like this should become a class, with all those private steps being private f()s from it. Tests would have plenty of access to those f()s, most of them not tagged as 'const'. Fortunately, tests are small, and compile-time prone to be implemented. So, tests are unlikely to cause bugs - and easy to fix if that happens.
Pros: each test would be executed only 1 time .
Cons: kind of a "risky" design .
b) Any f() like this stays the same, but its steps could become lambdas, in order to be tested, and this would be made inside that "big f()", each time anyone calls it. To avoid repeated tests execution (hence consuming performance), they could only communicate in case of failing. Since they are compile-time (according to your examples), the optimizer could solve them, realizing that they work, meaning they would do nothing, and then it could decide to eliminate them at compile-time, "because they are useless" (unreachable code). For the tests that accuse an error, the programmer should fix them right away, in order to them become potentially "useless" too - thus eliminated too.
Pros: design continues to be as safe as before, despite tests intrusion .
Cons: tests would be processed several times, unless the optimizer decides to wipe them out .
1
-
11:22, "Dream Job" doesn't exist. If it is 'dream', you'll have a low wage, for what you deliver. If it's a "cashy" 1, then it's good only for you, crazy 1. All jobs suck hard. The only exception is if 1 can do something that nobody else can. But even so, the dude will earn due to "monopoly" (of a talent), not because of all his technical skills. 22:49, indeed.
27:21, my code is spaghetti. If it's readable or not, it's a matter of taste. Computer likes it.
30:07, brazilian meme.
1
-
1
-
1
-
1
-
Continuing to compare it to Codeblocks IDE:
26:12, I use to be satisfied with PgUp/PgDown, for entire page jumps. If I want less than that, hold Ctrl and hit Up/Down: the entire screen rolls towards my cursor. This can be executed using only the right hand.
26:34,
1) If the idea was to print bar many times, it would be needed to put the cursor over it (no need for selection), then hold Ctrl and press or hold as many 'D's as bars I want.
2) Instead, to replace it by foo, just like you did, I 1st select foo. Double click on it or, starting by its leftmost, Ctrl-Shift-Right (all by the right hand!). Then Ctrl-C (copy). Down, to bar leftmost, select it to overwrite (as previously shown), then hold Ctrl and press V continuously or not, to replace it for as many foo as I want.
3) It's also possible to replace it vertically. On foo leftmost, Shift-Down (selects all the line), Ctrl-C (copy), Down (reach bar), hold Ctrl and press or hold V, and it'll keep adding foo vertically, even over bar.
27:27, this was fast indeed. I have to hold Shift and 5xDown (or select all the paragraph with mouse), Ctrl-C, to only then start with Ctrl-Vs.
28:20, hold Alt + Right/Left I can switch between opened tabs. If different projects are there, it can reach them. I prefer to focus on only 1 project at a time, so those tabs use to be files towards the project.
1
-
1
-
1
-
1
-
1
-
1
-
5:31, I don't know what is so bad about C++, like these kind of people use to say. It has all those types you mentioned, like classes that are not defined at compile time (interfaces), "simple structs with methods" (classes), foreach as algorithm and in the language core too, optional types, and so on.
7:15, C++ has the optional type, for a type that may be valid. But has a better solution, if 1 adds the GSL library, the NotNullPtr class (or some name alike), providing Zig's not nullable. It's also possible to develop your own pointer like that, and it doesn't take much longer.
7:49, so there's no C++'s namespace on those languages, huh? It works like a surname for a library. Each 1 has its own, so name conflicts never happen. It's also possible to dismiss yourself from typing that all the time, if you are sure it won't conflict.
8:51, copied from C++, which also has them as default parameter, meaning that 1 doesn't need to explicitly send them on initialization. 9:00, and if "you forget to clean things up", it'll do that for you, no messages needed. 10:05, it means 1 doesn't even need to do a deallocation.
11:20, people are contradictory: they love the "error or variable", for a variable, but at the same time they are afraid of "NULL or a pointer", for a pointer! What's the logic?!
18:57, yes, undefined means it'll initialize that memory taking the "trash values" left in there, from other variables previously freed (no longer exist). C/C++ has this by default (faster initialization), meaning 1 doesn't need to lose time typing ' = undefined'.
23:26, actually it's much better, because it's supposed to has several public f()s, only asking you to type 'public' keyword 1 time only!
23:43, 1) f() is not attributing something necessarily, so there's no need for the = operator. 2) f() doesn't know what the user will do with the variable (changing it, for instance). That's why it doesn't use to be 'const', although it's possible in C++, just not recommended. 3) Specifying the returning type improves compilation time. In my experience, it's better/safer to declare it automatic (fn, var, auto, depending on the language) during development, and switching it to its explicit type, after finished.
27:08, C++ is smoother, dismissing you from typing usize, deducting sum and the returning type as int, due to its default for integer literals.
1
-
1:18, prove why Java is easier. Garbage collector is not a reason, since it's bad, clunk, not elegant, and I believe slower than destructor.
1:38, it's half the C++ performance. And C++ can be very defensive, if you code it for that.
2:24, pointless. C++:
auto s = "Low Level...";
int32_t x = 5, *p = &x;
And C++20 has native optional strong type checking, via concepts. Templates were never the issue, even back to 1998, if you use some smart tags.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
Other things I often use on C::B, and I don't remember you had mention:
- Bookmarks: Ctrl-B on different locations. Then hold Alt, and PgUp/PgDown (hold or not) to navigate 1 or more times through them (without release them, in this last case).
- Applying current syntax highlight to any file extension opened. It "doesn't make sense", but I find it to be much pleasant to read that way. I just need to add that extension in a menu, for applying any current highlight I have at the moment. Easier to memorize, due to key colors on key locations. So, often I use to open files on this IDE, instead of text editors. Because these apply highlight according to the file extension, with iron fist.
- Abbreviations: Ctrl-J expands some user custom keywords to code. It's possible to set variables dynamically, so that it'll open a menu to type what variable will be placed in what location, among the code expansion. I can put an entire big fat class there, with plenty of variables.
- Jump between words, horizontally: hold Ctrl, and Right/Left (hold or not).
Let's say I have an abbreviation for a for with const iterators, and for some reason I don't have the nonconst version. So I type rngfor and Ctrl-J. It expands to:
for (const auto some_itr = obj.cbegin(); some_itr != obj.cend(); some_itr++);
So I want to cut const and the 'c's. I can't do that in 1 replace. Plus, it could erase other 'c's. So it's faster to go Ctrl-RightRightRight... till reach any word to delete. Then Ctrl-Shift-Right (select word), Del (all with the right hand).
- Default code: if I have a template of a project, this is faster than load from some other project, and saving as...
1
-
1
-
1
-
2:24, it's hard to figure out how this can be better than Codeblocks. Cursor up/down I can do with arrow keys. Insert mode is default, I can type anytime. Visual mode doesn't need special keys: it already starts selecting all the block by holding Shift-Down (can be done by just the right hand). Ctrl-S (can be left hand only) to save file, Alt-F4 (left hand) to quit.
4:36, holding Ctrl-Right (can be right hand only) does that. 4:44, same thing with Ctrl-Left (right hand too), for backwards.
5:09, this is nice. I don't know how to do this 1. But holding Up/Down or using the mouse I will take 1s more. So it's not big deal to me.
6:21, for that I need Home (1 or 2x, depending on its config, to reach leftmost position) , Shift-Down (select line), Del. Or 4x left-click with mouse (2x for selecting the word, other 2 for the line) and Del. 6:26, Ctrl-Z/R is enough.
6:40, that was fast. I would need hold Shift-Down till the return line, then Ctrl-Shift-Left, to "undo" selection till return's left side, and Del (all can be made with right hand). 6:54, holding Ctrl-Shift-Right (to select how many words), and Del (to delete all at once) - right hand recommended. 7:04, same thing for backwards, exchanging Right by Left.
8:00, I can type left or right that 0 without worrying about modes. I just need to use Right/Left arrow to move the cursor.
9:00, holding Ctrl-Shift-Right it goes selecting by words. I don't need mouse, despite it being an extra option. 9:20, Ctrl-C/V is almost as fast, can be made with left hand, and won't make another line, because the new line character wasn't copied on the selection.
9:34, C::B is faster on this 1: hold Ctrl-D to duplicate the line how many times desired.
9:47, Shift-End select the line, but the new line character. Then Ctrl-C and go to the other, Shift-End and Ctrl-V. 10:00, that was a nice exchange. I've Ctrl-X to delete and memorize, but could not do that on an exchanging. I would have to paste the selection above, and then Ctrl-X on the what should be replaced over, to memorize it as the new 1.
10:35, I use Home to reach the leftmost side, then hold Shift-Down, till selecting all the block + the other piece. Then Ctrl-D to duplicate it.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
4:10, yeah, she is too thin... not fat enough, I mean, not hot enough (lol).
15:54, I didn't understand. Wouldn't it be "old enough" to use it? I don't know, are young women using Snapchat nowadays? Didn't it bankrupted?
21:36, she said she "almost proposed to him" (15:43), the guy who only asked for her Snapchat. Unless she is lying (probably), she is not that hard.
22:40, I think the male game she means is the natural behaviour, which sounds to women like a game: ghosting, not marrying because of their past, getting away from relationships (due to obscure reasons), and so on.
41:03, men do this all the time too.
1
-
1
-
3:30, abstractions are the best thing, but can also turn back against the dev. For C++, I take a FP approach by default, until some variable can cause too much damage, if changed wrongly or from a wrong place. Then it goes to a class, to control everything about it. I 1st start with free things, then tie down some critical things - "decoupling" is not welcomed for those cases. So my code has many more free f()s than classes.
Complexity is not inside only 1 f(). If it's certain that a bug is inside 1 f(), it's just a matter of (short) time to be solved, doesn't matter how complex that f() is. It's like a lion trapped in a cage: just study it, and tame the issue.
The nightmare happens when 1 needs to travel throughout the project f()s, searching where it might had started. This is the main reason to write classes to restrict who can change critical data.
Let's say someone is coding a football (soccer) game. It could has a class for ball, players/actors. To coordinate when a goal is made, and its consequences, changing variables in more than 1 class, I use to have a class to tie those things together. It could be called referee. So public Referee::verify_and_change_if_goal would be the only or few f()s allowed to call private f()s Ball::goal_restart (to put ball in the middle of the field) and Player::goal_restart (to put players into their half of field, in certain locations, with some random variance towards that location, to seems more realistic, less robotic) .
So that Referee public f() can change the world, from any point where its object appears. Bad design! Actually, no. The verifications would be made inside Referee (lion in the cage), only changing variables in case of goal. So doesn't matter if it's called several times, even by mistake: the worst possible thing is to loose some performance; it won't ever bug the game. It doesn't even matter if the code grows up to 1 billion LoC: those things will continue locked.
But let's imagine the worst scenario: some internal error happened inside this chain of calls, and junior dev decided to shortcut it, creating his own way to change variables after the goal:
1) He would get compile errors because, let's say, the main f() who calls the Referee public f(), and now is calling junior's, is not 'friend' of those classes. Junior turnaround it:
2) made the main f() friend of all those classes, so that he can write his own way. On the next error, some senior will see the class definition, and think: "Wait: why main is friend?!" . But let's make it more complex. Instead of that, junior:
3) pulled Ball::goal_restart and Player::goal_restart to public area. A senior may think those were always public. This is awkward, because some error might happen, by calling 1 f() and not the other (i.e: Ball's but not Player's), since they are now decoupled. But this could be avoided, if they had comments on their classes declarations: DO NOT MADE THIS PUBLIC!
4) Junior rebels: made all the classes public, deleting those comments. FP rules once again! The security system is now completely destroyed! Well, senior devs should suspect all is wrong: 'everything public' is the sum of all fears!
1
-
1
-
1
-
Thumbs down for this obscenity, of course.
2:17, nothing can piss off more a developer than a company trying to make $$ with his project, offering nothing. I totally understand. What was its license? 2:47, yes, but that doesn't mean you are willing to work for free - or even in a cheap way.
4:06, I wish him success on this.
5:07, this definition is wrong. The world is not free, so software is not too. The idea is to keep it free as long as it is not used to make $$. 6:24, nobody wants to work for free, when life has its costs. This is slavery. 7:15, neither the creator nor (and much less) greedy f** companies! 7:29, you are being hypocritical: you are maliciously turning an altruistic work for knowlegment and reputation into slavery. An open source movement would never exist if that was the idea.
8:16, let's dive into piracy, clone all apps. They show us apps, so better they be lucky trying to make $$ out of them. 8:35, he was naive to not read the GitHub license, which allows anyone to put his/her own license over the creator's, once he/she modifies just a bit of the original project . Thus, I decided to not put any work of mine there - and never will.
14:07, for the last time: he was not expecting $$, he just not wanted to be turned into a slave! But he was too naive to not had read the evil GitHub license.
1
-
1
-
10:00, probably is skill vs number of people (average for up to 10). So that 20% are (0.3 / 0.05 = 6)x devs.
10:38, at chess, this graph not only is precise, but also it happens to so many people, that it's not absurd to take it as a "fortune teller" about someone who is going to learn chess.
11:53, I think it's about the same, but removing the vale of despair. This phase and before, probably happens on the very beginning. That's why it's missing here, which intends to take visions from professionals. So I think from junior to somewhat above it, the perception about software quality is optimistic, vs the pessimistic from a senior and beyond.
15:35, perfectly safe C is probably unbearable, due to UBs everywhere. But C/C++ working code, passing good tests (edge cases only/mostly) is achievable fast. Plus, there are awesome tools nowadays, even for UB. So, if development time is a thing, Rust won't be the best choice.
The problem is when a company abuses this fast pace productivity of C++, taking as "lost of time" automated tests, applying tools, writing your own tools. For these scenarios, Rust probably is better - not for the dev., but rather for the bosses: I mean Rust is the harness, boss is a stupid rushing horse .
23:13, C++03 had this, but split: words in the constructor, values here. It's better for dealing with legacy code, since it dismisses us from having to change the initializations.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I'm more of a mercenary-mind about software: $$, no deadline and a decent language are pretty much all that matters.
My thoughts about the questions:
10:45, 1st: I don't care. 2nd: Even the worst boss would not make any difference, if he "doesn't get in the way". 3rd: I don't care. 4th: I don't want to go to a conference. 5th: I don't care.
11:55, I don't care about all of that. I could alert them, but it would be all. I would not lose any sleeping night.
12:55, 1st and 2nd: I don't care. 3rd: I had those 3 goals above. The rest is garbage collected. 4th: I'm not excited to achieve anything, working to anyone, much less for a $$ hungry company. I don't "fall in love" for this work anymore. But that doesn't mean I would not fight for the company's goal - unless that was something evil, like censorship, privacy invading or something alike .
15:15, if the company wants to blow itself down, what can I do? I would try do advise, though.
1
-
1
-
1
-
1
-
31:02, I don't know if this is UB: '(pos) && (n = (pos)->next)'. Compilers use to be allowed to choose which portions of a cmd they want to solve 1st. So if pos == NULL, could it made 1st n = NULL->next?
32:04, just answer: what of those 2 lines would you prefer to write? Directly the big for (uglier and more prone to errors) or the short macro? 32:09, it's just the header of a for loop. There's no way to make that without a macro.
34:00, I stopped using goto like that, as a f() destructor, when I moved to C++.
34:21, as the same way as 1 adds { } to a for, for more than 1 cmd, he can add them here too. So it's not limited to 1 cmd.
35:15, it's more D.R.Y.-ed.
37:30, I use CamelCase for constants. I don't know, upper case seems "frozen" to me.
1
-
1
-
1
-
1
-
19:05, still about this code. Its returning value is inverted, considering that 0 is false, otherwise true, converted to int as 1. This is in the core of C/++. So this can (and will) cause bugs. I can imagine: if (test()), hoping that the test passed, when it got a NULL! And UB! Ok, sanitizer would get this fast. But let's not be bad programmers just because of that, shall we?
I know that, in ancient C, the 'main' f() got this absurd convention for some reason. And someone could say that this 'test' was made an "entry point", thus trying to follow main convention. But 1st, (at least) C++ has EXIT_SUCCESS/FAILURE, to let us forget about this. 2nd, I'll assume this was just a no-excuses mistake.
So, how to fix it? It's not possible to just exchange those values, since bugs would start to poping-up. If I would alone in this project, I would just create a C-enum, like:
enum test_ret { TEST_FAIL=0, TEST_PASS };
(The explicit 0 is due to 1 had once be the default. So I don't trust enum defaults) . The important thing is to tie the failure to 0 (false).
This would be enough, since I respect global constants. Not just because it's a C++ Core Guidelines rule, but also because I have personal experience about that. People underestimate literal numbers danger.
However, working in a team, it'd has people writing things like: if (test() == 0), and the enum would be implicitly converted to int, generating bugs, if nobody hunt those call and change them by hand. It's what I would do, after the enum.
If they were too many, risking the team write more of them than I could fix, I would change the enum to 'enum class'. It'd cancel the implicit conversions to int, causing compile errors. So people would be forced to see the enum class declaration, and its global constants - any IDE would open its file and location.
Even so, there would be people just taking a glance at it, thinking "Ah, some idiot changed it to enum class, thinking it'll make any difference" . So if I start to see many casts to int, like if (0 == (int) test()), the issue still would not be solved.
Then a more drastic solution should be taken. I would change the 'int' returning type of test to something not declared before:
CALLING_A_MEETING_TO_REASON_ABOUT_THE_STUPID_TEST_RETURNING_VALUES.
Compile errors popping up. The idea would be to stop the entire production line, making the new-feature-addicted boss freaking out, risking my job. But it should be made before this gets out of hand - some decision of not messing with working code. To get the boss hallucinating, could even put the time: MEETING_AT_10_30. He would appear sweating, pointing me a knife:
"Guess what? Nobody steals my job!"
"I don't give a crap about your sh##y job. I'm paid to defend the company goals, which are above you. So I'll keep that, until a get done with this sh##ness, and quit to wash dishes, which is a better job, thus paying more!"
1
-
1
-
1
-
26:00, I don't even remember when I had some serious issue, regarding to pointer indexing in C++. I wrote a class that inherits a container, and checks the index. I can turn that on/off anytime, for the entire project, by changing just 1 line!
It's also hard to get a trouble about pointer dereferencing, because everything is 'begin() + K, end() - N', valid range of containers. There's no space here for pointer issue. And when I send a nullable pointer to a f(), it uses to be null by default, which means I want to give the user the choice of sending it or not. In this context, it's obvious that I'll check the pointer soon. If it's a 'const char *' (for a string literal), and I want to earn some performance by not checking it, I set its default to "". So I don't have issues with pointer at all!
28:39, he meant that failing on those checks forced him to give up on pointers. These are much faster, because indexing always come all the way from the beginning. And failing to UB is not an option.
1
-
5:53, I use to not include in advance. I wait for a compiler error ou the actual use of something by the lib. This way I can avoid unnecessary includes.
6:00, Const Correctness Principle: 1st write 'const', only thinking after a compile error. Thinking is a waste of energy, avoid doing it.
And whenever I write { } for a f(), I write its 'return' right away. So I avoid the missing return UB bug. On modern C++, a more consistent defensive method for that is to declare the returning type as 'auto'.
6:26, A << B is the highest level possible thing: B is being threw to A.
7:26, for a 1st approach, I think assert is better here: 1 line/cmd. I know it doesn't close files, but this is the 1st, and not opened.
7:33, you are throwing away 1 of the best features from C++: get rid of things automatically.
7:35, for apps, I use namespace std, to be more productive.
10:30, const auto ptr = std::find_if (new_begin, line.cend(), ::isdigit);
if (ptr == line.cend()) break; // There's no digit to the right from where the find started.
leftmost = *ptr - '0'; // '0' keeps portability. Don't use a number: unworth to be memorized.
new_begin = ptr + 1;
10:59, atoi works with char *, not char.
16:35, std::map does this. I use to implement this as 2 std::array.
16:42, this could had been just: if (gTable[i].str == slice). If str and num where 2 std::arrays, the whole f() could be:
const auto ptr = std::find (str.cbegin(), str.cend(), slice);
if (ptr == str.cend()) return -1; return num[std::distance (str.cbegin(), ptr)];
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
11:34, well, if in C 1 write a struct with data and pointers to f()s inside, I believe it'd has the same or better performance than C++'s. But there would be several disadvantages, that I think it would not be worth to be called class:
1) Everything public. This is a disaster for complex projects. It's already a crushing factor, 1 should not use.
2) Pointers could change, pointing to another f() with the same signature.
3) If a class can't hold from public its things, defeats the very propose of it: code security.
4) No inheritance, making things riskier by using composition.
5) No constructor, leaving the programmer to deal with dangerous C initializations.
6) No destructor. Despite nowadays C has a kind of "general destructor" for any kind of variable, it's up to the coder to call it right when the object is created. It's not automatic like a constructor.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
13:30, I use Codeblocks: it's still an IDE, with plenty of modern features and key-shortcuts, while being middle-weight. I don't feel it slow.
15:00, I think you can say C++ (15:13) has a hostile behaviour, mostly due to some possible undefined behaviour, but not hostile design. It's pretty good at hidding complexity - better than any other language, I would dare to say. It also has compact syntax, which leads to more productivity. 19:27, speaking of that, I find C++ good even for simple/short projects. It doesn't force you to use any unnecessary complex tool.
1
-
10:10, Clang compiler has a tool to "trap" overflow and UB. So it'd halt this app. Case closed.
11:33, it's UB because "it doesn't make sense to do that". If 1 wants to 0 the var, just attrib. that value. Or if wants to rotate, which is a slow operation, use a 3rd party lib. for that, or write your own. C doesn't want to lose precious time checking that. And I think the lang. is right about that. This could be avoided by the discipline of using 'assert' f() call before any operation. C++ offers an even more interesting option: wrap the var into a class, making the assert calls implicit/automatic, meaning: even when you forget them! Remembering that even on C those assert calls execution can vanish by just #define-ing NDEBUG. So, for releasing version, they won't take performance.
16:14, hmm... delicious. Bookmarked to read later.
18:47, in C++ 1 can make a class, holding a pointer, which always automatically check to forbid it to be nullptr. It was even already deployed, on the GSL lib, to reinforce C++ Core Guidelines.
23:00, he's wrong because, despite the ptr_byte is "walking over the ptr mem", it's always taking the least byte from value. memset is not forcing him to use the least byte: he used that, instead of the entire value. The result was actually: memset (ptr, (unsigned char) value, num);
28:50, this may be C-only. I never saw this in C++.
1
-
1
-
@mikehodges841 TDD for small/tiny f()s can be good. Even so, often the dev. for a while is not sure about the f() signature. So I think a "hybrid" TDD is better: code the small f() till discover its signature; once having that, write the tests, and then complete the f(), which now will become much faster, due to "the blessing" of the tests. It means it can be completed with less thinking, saving energy for the long run.
However, in my experience, big f()s (2-5 screens) doing lots of things, either directly or by calling others, are hard to predict in a TDD way. And also have this editing issue.
Good thing C/C++ have a kind of tight syntax, making each test fill only 1 line. So it's easy to turn some of them on/off, when broken. By macro, they may not even be compiled too.
1
-
1
-
1
-
1
-
2:09, 1 of my biggest bugs in life came when I thought: "3 lines for these 4 ternaries... I guess I will wipe this into 2, elegantly" . I reviewed it by mind and... approved! Those lines held about 8 possible combinations. It happened that 1 or 2 of them were wrong, well disguised among the others. So those use to seldom happen.
To get things worse, there were 2 other parts of the code that were more suspicious of being guilt, so I took a while looking closely to them. Automated tests would had get that easily.
3:40, I guess there was code before and after that break. The problem is that in C/C++ 'break' jumps out from blocks of switch, for, while, do-while , but doesn't has this power over if/else ones, as the coder unconsciously thought at that specific moment. So the break was applied to the 1st block above those ifs: the switch, jumping over the processing of incoming message.
I once got a bug from this. I never wrote a tool for this 1, since it never was a recurring 1.
For this AT&T there were some solutions to replace the else-block, trying to not duplicate the code to where it should jump:
- Make it a f(). Bad design, since the rest of the project would see it, and may call it by accident. So boilerplate code should be added, to remediate this.
- Make it a macro f(). Despite I don't use to have problem with macros, I agree that it would be noisy/dirty code, depending on its size.
- Use a label after the END IF, to be reached via goto. Better, but this goto could still be called from any place from this case, at least.
- Lambda f(). I think this is the best 1: break would result in compile error, and return from any place would exit at the right spot. However, this was C, and neither C++ had lambdas at that time.
1
-
1
-
1
-
1
-
1
-
Shockingly clear vision!
9:17, weird enough, those 3 topics work for me. 1) By far, most of videos and channels I see/follow are pointed by the AI from my Youtube channel, which is pretty smart, because I trained it well. 2) Judging by Youtube shorts (I don't use TikTok), it's more addictive than I 1st thought. 3) I'm used to variety of content/midias: shorts, videos, music, games, movies, series, coding, writing, reading, to say the least.
1
-
1
-
1
-
1
-
1
-
1
-
2:40, this is unfair: that cartoon showed interpreter reading legal instructions, while the compiler saw errors. The actual difference is that interpreter will show to you your errors at each instruction, while compiler will list all your errors, not "leaving you on your own" - of course, compiler errors can be combined to raise new ones, but that is a minor a detail .
3:28, C++14 (since 2014) has a much better solution:
const auto foo = "hello world!"; //foo is deduced as 'const char *', but you can convert it to 'std::string' at any time.
const auto bar = 23; //It's an int, because literal number defaults to int.
const auto baz = 42F; //A float. Just trying to complicate matters.
constexpr auto fun () {
return bar + baz; //Returns float: int + float = float. Always the more detailed 1 wins. Even fun will return its value at compiling time.
}
All of that at compiling time, not losing performance, as it would happen at runtime.
1
-
1
-
1
-
1
-
1
-
1
-
5:00, come on, they are... kind cute, in their own way.
6:00, she is having a bad time on dating, due to her "hugeness" (height). And "all women" use to read those books, saying that they have to try rising up their happiness by force, anytime a depression threatens themselves. That's why "she is crazy", or behaviouring like that.
Anyway, as my experience hearing other men says, men indeed get intimidated when the woman is taller than them. It's statistical.
9:30, what "I hit a deer" means on dating?
17:48, wanna go straight to hell, after dying? Go be a stripper.
32:00, she means she is not a cheater.
1
-
1
-
1
-
1
-
1
-
1
-
15:22, since class is about protecting data, I design them towards this. So there's no "obscure contract": the base class holds some data, and maybe some f()s, if they are pretty tied to it, technically. It's not a contract, it's an independent, ready to work class. A derived class should expand its data, or else should be reduced to just independent f()s, in most cases. This also brings the advantage to send just the base class, to f()s that only needs it, allowing a possible performance gain by coping it (if small), instead of referencing it.
16:30, the best way to do this in C++ is to avoid the abstract base class: put its methods on a macro, using composition on the derived ones. So the compiler will build the classes as concrete ones, not interfaces, gaining lots of performance.
26:10, this is pretty awkward. It should be just 1 class, and not an interface by default. And if many f()s should not be allowed to change users, almost everything here should be made nonpublic, allowing just a few f()s to change it.
1
-
1
-
1
-
1
-
1:27, you could get rid of that label and its goto, by changing this to else.
Nowadays, C can get rid of resources automatically. Why didn't you use it?
2:00, if you want to use dangerous literal numbers, at least create a global constant: 0x_4545.
a) If a typo occurs, compile error. b) If that number ever changes, you can detect all of its occurrences by the compiler, not needing to rely on code editor features.
Another thing: all sorts of fields are being accessed in what be was a 'char *'. If that recv function (1:10) failed to fill 'databuf', all sorts of runtime errors would occur. Could recv fail?
2:30, no check for h->len, huh? Hm, I know to where this is going to... 7:10, yep, I knew it. However, I would write this as:
memcpy (msgbuf, data, h->len > 64 ? 64 : h->len); //At least it would get what could fit in it.
Whenever I mistake something like this, I write my own f(), like memcpychk, which would compare size of both arrays forever (at least if NDEBUG wasn't #defined). C++ provides additional security, by allowing to give same names. So it'd require namespace specification: ::memcpy for C's or my_own_namespace::memcpy for yours.
1
-
1
-
@timmartin325 Repeatability is relevant because, as I said: "3) A change in the code can lead to a bug that once passed in a test".
I agree that human tests catch bugs that are impracticable by automated tests. Once I noticed a bug that took 2 years to arise, in a user point of view. It was caused by an overflow in some bits of a variable, that had pass through an optimization rework. That was expected, indicating the hit on the wall. However, the variable worked with +1 for reading and -1 for writing, to fit the bits field. Outside the class, a local variable (representing the field) in a f() work with the values normally. So, when hit the wall, some tasks were made, and the value has been writing back to the var. The point is that later I implement future checks for that memorized value, but it was wrote not with the overflow value anymore (because it wouldn't fit) - instead of it, a reset value due to bit truncation.
But this was not enough to raise the bug, because the +1 for reading make it come back to an acceptable value, at the beginning. And combining that with certain characters alignment, consequences passed to be acceptable in a broken geometry: starting (only) from the end (triggering the overflow), completing it at the beginning! (Geometry could be broken, but the alignment should stay in the same direction) . So the victim became unmovable. Plus, a bad luck of characters too close hid the cause.
To appear, it has to attend to several steps: the overflow not entirely solved locally, the reset (which could crash or lead to absurd) being hidden by the +1 (for read), some specific alignments, certain characters, starts with character(s) in the "wall", complete it with character(s) at the beginning. And a bad luck make me took more time than I should. I solve it fast, however - I may be inspired.
I baptized it as Age of Aquarius Bug:
"When the moooooon is in the 7th Hooooouse
And Jupiter aligns with Mars
Then peace will guide the planets
And loooOOOOve WILL STEER THE STARS!"
1
-
0:02, pretty? Signed var is faster.
0:10, in C++ it's possible to make a class that always implicitly checks if the pointer is nullptr.
1:17, I would not use linked lists neither, which rarely are faster, can't be used in STL algorithms, and forces you to make raw loops. For instance, if 'e' was a std::vector, this whole f() would be dismissed:
const auto Result = [](const std::vector <int> &e, const int search) {
const auto Result = std::find (e.cbegin(), e.cend(), search);
return Result == e.cend() ? null : *Result;
} (e, search);
1:24, C++ containers use to have iterators delimiting begin and end, putting the programmer in a range loop by default. For instance, std::forward_list performes a linked list in 1 direction, and has begin/end f()s to give those iterators.
1:55, or just use std::vector, which will free the memory, if its object no longer exists. It hasn't the use after free protection, but it's possible to wrap it in a class, to check that automatically.
3:20, but if that variable must travel alonside f()s, as read-only, and be changed only at the Nth f() called, C can't protect it. C++ has the solution: hide it in a class, making that f() friend of it, so that only it'll be allowed to change the variable.
4:01, C++ has the attribute [[nodiscard]], meaning that a compile error will raise, if the return value is not treated.
5:40, I always use pedantic, because it has good rules. But Werror forbids me to run the app. I always end up cleaning all warnings I turned on, but not always I want to do it right away, which may be less productive. Same thing for implicit conversions. So I don't turn on all warnings.
So we can realize that using C++ is a big improvement for defensive code, at least over C.
1
-
1
-
1
-
1
-
7:25, I once made a nasty bug by refactoring 4 lines / 2 cmds to 1 line / 1 cmd. They were all made by ternary operators, nested or not. I mentally checked "every" possible case. It ended up being correct in 6 of 8 cases, as long as I remember. It was hard to catch, because 1) it was hard to made it appear (seldom seeing, but never gone), 2) it appeared after tons of things happened (reproducing its scenario could produce a false positive, regarding to its source) , 3) and I had a false lead/clue, which took a time to realize that. Hard to test, happen and promising false clues.
Automated tests would catch that right at its birth. But there's a question that doesn't want to silence: if automated tests are necessary for every bit of refactoring, would them in the end taking more time than catching a bug when it finally happen?
1
-
1
-
1
-
1
-
0:00, "....Nice day for fishing, ain't it?"
I made several tests of switch (different versions) vs if-block. Switches w/o default cases, w/o a case for numbers < and w/o number > than some value, and so on. I found that switches have a more unstable performance, some faster, others slower.
Array use to be faster than switch, by ~15% according to my tests. I remember only 1 combination of switch beating 1 array alg. So, it's better to rewrite this as:
const auto Options = "qcned";
const auto ptr = strchr (Options, optionbuf[0]);
if (!ptr) break; //Default case of switch.
#define _(Task) handle##Task
const auto funcs[] = { _(Stop), _(Continue), _(New), _(Edit), _(Delete) };
(funcs[ptr - Options]) ( ); //Cases are solved on the indexing. Function called right away.
1
-
0:01, when I code, it's always gorgeous! :elbowcough:
0:08, I disagree. Modern C++ features are higher level ones, easier to use and less prone to errors. It's becoming harder to get things wrong with it! The price is raising chances to write slow-prone code.
0:55, TABs are better: uses 1 char and can be configured equally for all the team. I use TABs with size == 2. 1:05, neither draconian nor pedantic: the team must has the same rule about it, because it's an annoyance to see messed indentations. Once the team agrees about its size, use TABs. 1:20, are you saying that if TAB has the same size, it still may differ from 1 code editor to another?!
2:37, I can agree that, once the f() is done, a typedef is the better returning type. However, during its development, auto is much better:
- More productive: it doesn't require to often change its type.
- Defensive: if forget to return, it'll be deduced as a void value, raising compile warning if attributed to some variable later.
5:25, I don't use to have this "diamond problem". Mother and Father are not the same person, so each of them should inherit its own exclusive object of Person. Child is yet another person, not tied to parents. So it should inherit Person as well.
I use to inherit classes when the derived 1 should has the power of change data of its bases. Except for Person, it's not the case. In real life, a child can change parents behavior by communication, not mental/physical interoperability. So Child should not inherit parents. Instead, its constructors should receive parents objects as 'const &', read-only stuff. Just as in real life: child receives a read-only genetic material from its parents, and is destined to be an independent person.
6:30, interface is too slow and just a bit higher level than a normal class. I don't use it, it's not worth. Plus, for those who use it, a better approach than an abstract class is to make a macro out of it. Then, by composition, call the macro inside the "derived" class, making it a normal class, 15-17x faster!
6:38, if a class has this limitation, I just make its constructors as protected, forcing inheritance.
1
-
0:44, wait a minute, is there no error about it? Is it just a matter of "being stupid or not"?
10:27, I'm 2, because:
1) If I delete a '//' C/C++ line comment, I have to decide about indentation right at once. While with 3, after putting a // (lefting a blank space before the cmd), to later delete it, what will happen to those "3 spaces"? Will they turn into a tab or 3 spaces? Does it varies from code editors?
2) Since I use to write pretty horizontal code, tab 2 saves me more space for comments at the right side.
I believe tabs are better than spaces. However, if each 1 on the team has the freedom to chose their own tab config., spaces avoid a mess.
1
-
1
-
1
-
1
-
1
-
1
-
7:14, the only "unsafe design" about those is that, when the vector changes its size, it's not granted to stay at the same location in memory, so the iterators keep pointing to old address. 1 just needs to "refresh" them. But this need exists only 1 time per allocation (size changing). This is not made automatically due to possible performance. It's like web pages that are always refreshing themselves vs those waiting for the user to do it: the 1st is more comfortable, but wastes performance/resources from the machine.
The operator [] hasn't this issue, because it comes all the way from the beginning. But has a performance penalty. I personally use iterators intensively. I only had this issue once.
8:55, agree. This is awkward because, for every 1 of the millions of f()s, the code will has this amount of lines. The way I use to do this is to write a macro only 1 time, calling it everywhere:
#if NEWC
#define arrpass(type, name, dim) type name[..]
#elif C99
#define arrpass(type, name, dim) type name[dim]
#else
#define arrpass(type, name, dim) type *name
#endif
Then f()s will be written like (doesn't matter the standard):
extern void foo (const size_t dim, arrpass (char, a, dim));
1
-
1
-
1
-
1
-
1
-
1
-
1
-
5:57, performance uses to work in opposite way: the more promiscuity between types the better. And C is specialized on that.
6:46, if the C compiler doesn't receive optimization flags (or -O0), it will pretty much execute line after line. But all flags for that manage execution at better moments.
8:50, as long as I heard, this purity means its f()s don't have side effects. But this is ideal world. In practice, you'll have to write side effects out of f()s (to keep them "pure"), which looses even the precary encapsulation FP offers. It seems to me that only needs a more complex project to see this falling badly, compared to usual FP, in the same way that FP does compared to OO. No wonder why Haskell has the motto: "Fail at all costs!" .
12:00, that's why C++ was created, which seems to me to be the best language for crafting tools. I don't know in depth other languages, but I saw a presentation showing C++ as "the language with more functionality" (compared to D, Rust, Java/C#). So 1 can go much longer than linked lists - and in a safe way, if he creates his tools properly .
20:13, in C the type volatile were used to deal with that. But since 2011 C++ has a STL lib that forbids data race at compile time. It's not as easy to use as higher level languages, but it's a pretty improvement over ancient C-volatile approach.
1
-
0:58, this is a common mistake, even among experts. Because we don't like to rely on the compiler ability to save us from a "performance mistake".
This is a simple 1, probably compiler does the right job of undoing it, whenever the initialization was unnecessary. But it's hard to put this practice on people's mind, on this niche. It's a cultural issue.
1:20, in C++ it's recommended to put: assert (number.length). It ends up not taking performance, because all those asserts might be ordered to not be executed, in 1 line of code - after debugging.
1:40, an easy to fix typo, immediately catched by the compiler, I wouldn't even take it as a worth mentioning error.
2:10, there's another problem: floating pointing should not be compared by exact values.
3:30, this for config. is so much more often than others, that I even wrote a macro for it.
0:01, this f() should return a bool:
return average - expected < abs (max_imprecision);
In C++ this would be result in bool. Or if a message should be send:
return "average is " (average - expected < abs (max_imprecision) ? "correct" : "incorrect");
I don't remember if this compiles.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
I had 2 major uses for goto. 1 of them is what you are showing, that I call it "f() destructor". When I moved to C++, I hadn't this need anymore. The other case was to jump out from the middle of nested loops. But after lambdas (C++11), I stopped using goto at all.
For maintainability, I would also pack this code with macros:
#define errchk(var, value, err) \
if (var == value) { \
perror (err); \
ret_val = EXIT_FAILURE; \
goto main_destructor; \
}
int main (const int argc, const char *argv[]) {
void *filebuf; int fd, sfd, ret_val = 0;
fd = open (BLABLABLA); errchk (fd, -1, "open")
filebuf = malloc (BLABLABLA); errchk (filebuf, nullptr, "malloc")
sfd = socket (BLABLABLA); errchk (sfd, -1, "socket")
//Do all the stuff...
ret_val = EXIT_SUCCESS;
main_destructor:
if (fd != -1) close (fd);
if (filebuf) free (filebuf);
if (sfd != -1) close (sfd);
return ret_val;
}
#undef errchk
1
-
2:53, this is so easily solved by OO...
5:50, you should has went to C++ instead. You would get a shorter/cleaner syntax and faster language, compared to awful Java.
For C, this can be solved by just creating a struct that carries its length:
struct ArrayWithLength {
int thearray[ARRAY_SIZE];
enum { size = ARRAY_SIZE };
};
But the company still has to write alternative f()s for all standard library, to check array size automatically, at each call. I recommend even to write an app to forbid the programmer to call unsafe libraries directly, by statically checking the code.
All of this is solved at once by just switching to C++. Its std::array has begin/end f()s, giving iterators for its limits, keeping the same syntax of any other container, throughout its entire standard library.
6:45, right at its 1st standard, C++ had a fully safe, modern, easy-to-use string class. It hasn't the \0 terminator problem, it keeps the size internally, it's compatible with all C libs and, for the user, since C string literals have implicit \0 in it, with std::string 1 can forget the terminator, even when expanding the string.
1
-
1
-
1
-
2:45, I once read a book by someone that used to code like that. It is pretty legible indeed. However, I don't code like that, because there's too much waste of time and energy by traveling vertically throughout the code. And there's also the worry about discapsulating things that, if not hold by a class or something alike, can then be called by the rest of the project, raising chances of bugs.
I prefer f()s that fit in 1 screen. They may be bigger, if there are more things that don't make any sense to be seen by the rest of the code.
6:07, I guess for small/tiny f()s, it's easy to know what tests 1 wants. And even if the code is from someone else and you don't understand it, if it has tests, it's possible to refactor it, even fast.
9:20, from my experience from timed work, I can say that often there are tiny interruptions. And if the programmer stops the clock at each of them, the resulted time is almost double. Examples: a) 2h -> ~3:30; b) 4h -> ~7:20.
It's not because I took too much time to go back. It's simply a matter of too many interruptions: a glimpse of an idea that you don't want to miss, someone talking to you, an uncertainty about the work, some stress, some feeling about hunger or thirst, a joke that you remember, and so on. It's inevitable. I guess that if someone makes a true effort to eliminate this, his stress will skyrocket. 10:12, I agree with you here, but it's not what happens, as I explained.
1
-
1
-
1
-
The author showed good skills, regarding clean and DRY code, automated style (including safety checkings) and knowledge of technicalities about C. But to me what really matters about senior or ace worker is the concern toward safety. He didn't mention this, at least not by words.
19:05, for instance, in this code I would point out some things:
- 1st, I would "rewrite everything in Rust"... Nah, it'd be in C++, which is indeed an improvement over C, not lefting anything behind. If the boss didn't agree: https://www.youtube.com/watch?v=O5Kqjvcvr7M&t=22s
- I would pay attention if linked list would be the best choice. It's only faster when there are too many insertions in the middle - that means a sorted list, somehow. Otherwise, a std::vector-like is much, much faster. For instance, if it's just a database, this sorted linked list would be slower than an unsorted vector-like, adding to the end, and removing in the middle by replacing it by the last. Or am I wrong?
- I would study the idea of changing that boss raw ptr to a unique_ptr or something higher-level: more elegant and safer.
- I would change that person::name, from C-array to std::string: more comfortable to work and almost no chance for UB, leading to cleaner code, since it'd require much less if-checkings by the user. But main advantage is that std::string is not a primary type (it's a class instead). So it's possible to later change it, by a user-defined faster container, keeping the same syntax to communicate to outside - no tons of time refactoring throughout the entire project . This would not be achievable with C-array, unless all its uses/calls where made via f() or macro - which nobody uses to do for it.
And would worry about that only if that std::string was a bottleneck, which is unlikely. But ok, let's imagine the worst scenario: it needs to be replaced by a fixed-size array, which uses to be 20% faster on the heap, only. Since it is not flexible as a std::string, does that mean it'd break its syntax, needing refactoring? Actually no, there's a turnaround: a tiny user-defined class, inheriting std::array (same speed as a C 1), and writing by hand all std::string specific functionalities, like += for concatenating. So all the work would stay inside the class.
In case of bigger name being assigned to 'name', an internal check would be made, as Prime pointed out. But not via assert, which would break the app - it could be 1 of those ongoing apps. Just an if, truncating the name to the size, writing an error to std::cerr.
But probably a fixed-size array could not be used: it has a limit of total memory per app. Since this code is making allocations, it suggests there are a huge number of persons. So it'd get a seg. fault. So std::string would be indeed the best choice.
1
-
1
-
1
-
0:10, I heard some bizarre things other languages do to inheritance. But C++ treats it nicely. I never got issues with this feature. For instance, that "diamond problem" doesn't exist (at least not by default).
2:50, I came from a C background. I even made my final college work in C. OO was a relief. Safer, easier, higher-level, better for crafting tools (key for improving the language) and also performatic, if the programmer knows what he is doing.
4:07, yeah, you are right: a simple variable can fit into that description, since it's indeed a "shared state of many previous operations" . But the key difference remains on the encapsulation provided by OO - at least in C++. FP can only offer global variables with a filter (setter). In C++ you can control who can even call its setters. This is a huge win! 4:17, but if it can't control who can call those f()s/data, its OO doesn't mean much.
12:53, interfaces are bad: start slow and eventually end up being bad design. See this talk: https://www.youtube.com/watch?v=aKLntZcp27M&t=720s
1
-
1
-
1
-
1
-
1:22, I guess they are related. Java must mean 'mess' in some idiom, and script would be 'easy to use'.
3:55, in C/C++, you can write:
#define var FORBID_ME_TO_USE_var_AT_ALL
...getting a compiling-error every time you use that.
4:38, C++ is more defensive here: you must explicit if the inner f() (called lambda) can access all external variables or only the ones you list.
6:35, 'Warning: setter made public' .
8:30, in C++ I like to kill that kind of if-block (leading to faster code), by previously defining failure/success as global constants of 0 and 1, tying greatSuccess to those values. Then, this whole if-block would become: resolve (greatSuccess);
1
-
1
-
1
-
1:54, yeah, this is really good strategy. The only issue is when working with a team: others might not have your concern towards safety, prefering to rely on their intuition. So, it's a good idea to shrink the verifying code. 4:00, if possible, an interesting way of doing this would be to pack errors as flags of a bits mask: a unique number for each of these errors. When any of them happens, it'd be recorded as the positive Nth-bit. So the asserts would always look the same, something like: assert_stats (blablabla).
The f() would check all errors at once, since bits work in parallel, taking less than a machine cycle. Like (in C):
if (Mask & (DUMMY_CLIENT | EXPECTED_CONNECTION | COULD_NOT /*blabla*/) == 0) return true; // All ok. Otherwise, switch-like below, for error messages.
Only if an issue matches, it'd make a detailed switch-like process, with error messages.
So it'd "feel easy" for anyone to just call the same thing, not dealing with different err messages at each call, for instance. C macros could even hide this into the f()s beginning: instead of {, write BEGIN, which would put { and call assert_stats behind the scenes. Later it could be as easily disabled, by just editing the macro definition.
1
-
1
-
1
-
1
-
1
-
1
-
7:07, I think C++ fits in this article even better than C#. However, it requires the user to develop "some feelings", if he chooses to use default behavior/resources, instead of developing his own defensive tools. For instance, my 1st thought after certain action(s) are set to:
- Check the result of a f() or algorithm right away.
- If what matters is index where a pointer stopped after an algorithm, I get rid of that pointer at once.
- For things that I'm used too, I write as fast as I can (favouring productivity). When something starts to be unique, I proportionally start to get slower and more reflexive about (favouring defensiveness).
When things get complex or I'm failing often (for whatever reason), I stop everything to write a tool to lock the right behavior forever, going back to be faster/productive. So things go inside f() or classes (yes, including setters as nonpublic) , only 1/few way(s) to reach them, putting as many layers of protection as needed (thanks to C++ high functionality), whatever needed to reach productivity once again, because I value doing things without thinking twice, in crazy fast typing fashion.
So C++ fits in this article purpose of proportionality production, according to its complexity. An example:
I was working from picking values from a string, by pairs. I decided to use its default behaviors. I wrote fast, everything works predictably. No need for fancy tools nor languages. Then I decided to optimized it, by using a pointer that made 2 steps per cycle. It got expressively faster. It was also fast to develop, and worked flawlessly. So I left the computer, with my 15487th easy victory using C++.
But my gut feeling said to me that I wrote too fast something that I'm not used to. So, calmly drinking a coffee, I made a brief reflection about it. Mentally I discovered that the 1st step from the pointer was immediately checked, as I use to do, but not the 2nd. So it would step beyond array boundaries on the last 1, in some cases, whenever the f() didn't return before. Easy check, easy fix, I just added 1 line check for that.
1
-
1
-
1
-
1
-
1
-
7:12, this guy missed the point that companies wet dream is to hire juniors only. This is a goal, but hasn't been possible so far. It's predictable that some of them would eventually ask, for its top programmers, to develop a language/library to help noobs.
14:06, you should, because otherwise it's prone to erros: similar blocks of code put your guard down, while a bug is hidden between them. But there are some caveats for that:
1) Copy-paste is good practice, due to saving energy, which will be important on the long run. I even timed that once: it took me 20h to create 1 bug due to that, meanwhile I saved energy many times.
1 just needs to be a bit more careful when doing that, compared to normal code. And soon being ready to make the number 2:
2) A common code for all repeated block. Not with ifs between them, which leads to bad performance, due to branches. Just the actual common code should be extracted.
14:36, nice statement. Is it yours? I heard that for performance, but now that you said that, it seems more real for abstractions.
22:28, if a language lacks encapsulation, it's over to me.
1
-
1
-
1
-
12:00, the standard is conservative: keeping a language with "all features" is already a lot of things. And since it aims to have backwards compatibility as much as possible, it's desirable to keep off things that seems to be "born to be external" tools.
I guess this view of not breaking things along the way allowed the language to exist, with success, for so long time.
12:14, chat: its syntax is a war crime. I find it to be quite clever: it's productive, since you type 1 thing, and it's likely to be correct - the context will tell exactly what it is. ie: you don't type defaults, saving energy - what would be an error, it's allowed under a certain context. I don't know how many languages have 'var' and 'let', but this last 1 for constant is stupid: it should not exist (const as default).
12:36, it could be worse: forced to use identation.
19:19, you can only be joking. With a bit of effort, C++ jumps to a kind of high level language. 19:55, for instance, when 1 makes an algorithm traverse from [begin; end), the pointers are always trapped. I never got a memory seg fault using that! Ok, only 1 time I fell for the "invalid pointers", when the memory was reallocated. But never again! And it was an easy to catch bug.
1
-
39:53, it's comparing the addresses they are pointing to, which should be y's, if x and y are in this sequence.
41:50, 0x7fff'ffff + 100 is UB, invading the bit for the sign area. The right way to write that check would be: assert (0x7fff'ffff - 100 >= a); And I guess the standard <stdint.c> lib has INT_MAX (or alike) constant for that max value, which should be used instead.
Plus, there's an UB on main too: not returning an int, when promised that. However, there's a compiler flag to catch that.
46:00, it's broken, because an UB was created BEFORE even the check for UB was complete! 48:35, precisely. 53:49, I disagree, because it's inflicting the compiler's freedom. Let's say a compiler deploys signed 32 bits represented as 31 bits, with the signal being kept somewhere else. This unnecessary way of checking might create unnecessary problems.
1
-
1
-
If this engine is for game, don't do it. Instead, make the game directly, only what is needed for it. Whenever something becomes generic enough, put it in a general lib of your own. In far future, if this ever becomes robust, only then you make an engine out of it.
I strongly recommend C++, or at least its classes and containers (from STL), to properly hide data. Game is a wild environment, eager to steal your data, which needs to be tightly secured.
1
-
@anon_y_mousse Games have a loop, having to keep values memorizes. So, goodbye to pure f()s ideal world, for instance. There are plenty of side-effects, often. It also takes important decisions, like which branch to go, according to those values.
So it's needed to avoid some unwanted f() to "steal" some data (to have access to it, when it shouldn't) to change it. If everything is public (global variables, for instance), lots of things will be changed at wrong moments, either by mistake or by a "4am hacking thought of design" - a "theft of data", compared to good design.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
0:37, to GitHub, doesn't matter your license: it'll always be free.
2:28, interesting, in a sense of freeing things from memory. The problem is that, when you finish a small task, it'll eventually need to be broke, to attach a glue to it, in order to serve to a bigger "ecosystem", throughout the project development. So 1 will keep going back to that task, to improve it.
Another strategy is to get the project in working state, even if in kind of a bad design. This gives information of how things should interact. Some things are only known in practice. And if things are decently isolated, like it's possible to do in C++ (private setters, for instance), details can easily be improved without fearing the rest of the project influence.
Nowadays, mankind is experienced enough to know certain strategies, to advance fast through a project, like automated tests, keep same patterns for everybody, decent encapsulation (as I mentioned, private setters), and so on.
3:00, autism is a severe condition, making the person almost incommunicable. You hadn't it, you were just introspective. 3:36, this is the proof: these are extrovert features. Just train those for a little while, giving them their deserved value, and anyone can acquire them. Autism would never be solved this way.
7:08, there's a mindset switching issue. It's unpleasant to do that, and thus may be hard when the time for the most unpleasant task arrives. So, if it take longer than should for 1 person, I agree. On the other hand, if it's not the case, it's better to start by the easy things, for motivation. When coding, I prefer this last method. For life chores, I think the other is more recommendable.
10:05, I prefer blocks made of 2h of tactical work, 1h of strategic work. But I guess you are not being entirely honest about timed work. There are small distractions, and if you stop the timer for those, being brutally honest, it'll yield almost double of that! By my experience, 2h -> 3:30, 4h -> 7:15-20. 11:00, maybe the headaches are caused by trying to suppress those distractions completely. I don't do that. Instead, I just stop the timer, think/do whatever triviality I want at that point (even if just a brief thought), and then back to work. Before reactivating the timer, I get a little focus 1st. I never had a headache in life!
1
-
1
-
1
-
Let's compare it to Codeblocks IDE (aka C::B):
24:36, it has a sharing config. file. It's a xml-like. But it's possible to do this at a higher level, through a small app it provides.
24:50, I remember an old version came with this instant replace all enabled by default. I don't use it, because I think it's "too aggressive". I replace all when I expect it to stop on the page I'm. So I double click on the word, Ctrl-R (opens a menu with that word already selected) , then I only have to type for what. So a new menu opens, I hit All. And I pay attention to where the cursor will stop:
a) On the same page. Perfect. And it was just a bit slower than what you did.
b) On other place. Then I Ctrl-Z, undoing that all at once. I rethink my strategy.
However, I still don't know how you made appear n and v on the same replace. Was it a regex-like replace?
25:34, I use Shift-Down (selects the 1st line), Down again, not releasing Shift (selects the other 1). Then only hold Alt, followed by Up/Down, moving those 2 together, even leaving both inside an if block. It can also navigate line-by-line automatically, if I hold both keys pressed.
26:00, for this I use End (goes to the end of the line), Del (deletes the new line character, making the line below rise to the right side). Then I go End/Del for each 1. At the end, the cursor ends at rightmost side. I only have to press Home, and it goes to the leftmost.
1
-
1
-
1
-
1
-
1
-
8:43, this mistake I don't make. If I worked alone, I can even describe the project from a high to low level, in different "layers".
9:23, ideally, code should always follow the best design. In practice, however, code influences back the design. This is faster development, but has a cost. I use to have my overall design near intact by the code, despite often undesired splitting classes for better access control, number/types of f() parameters, and so on. 9:44, particularly to the internal design of f()s, I don't see that much of an issue. If variables or small arrays are created and destroyed inside it, and a bug arises from them, even if complex, the region is "fenced" in that f(). So design won't be a problem: it won't require to understand messages, data flow, and others.
13:32, I use to write pages describing ideas, reports, failures, and sort of.
15:06, the vague guess of billionaire companies taking my code for free, to make $$, makes my alien try to break out from my chest.
1
-
1
-
9:05, I have some doubts about that. In 'You', from Netflix (1st season), the guy felt that the girl "needed someone to protect her". This is tuned to the man and woman dynamics. I don't mean that this is the best a man can expect from a woman, but it exists. And, of course, can't be something "made up", pretending or something alike.
The admire thing is stronger, though.
9:42, she is right. On the 3rd season, he was with another woman, and thought: "... and there is the woman from whom I felt in love: happy, kidding, full of life...". That doesn't mean that a "bonachona" (I don't know how to translate this) woman is welcomed to most guys. But, as I heard from "spiritualists": "whoever has his ying and yang balanced, attracts everything!".
1
-
1
-
0:48, I think the TODO fix needs to be annoying, if the idea is to do it soon. Whenever I'm coding alone, I use debauchery as a strategy to make me not to bear it for too long.
1:23. I use to write this as (much cleaner to me):
while (days > MIN_DAYS_in1_YEAR) { // = 365.
days -= MIN_DAYS_in1_YEAR - (IsLeapYear (year) ? 1 : 0);
year += 1;
}
1:32, there's a compiler warning flag against this "dangling else", advising to put { }, embracing the else from the 1st if: if (is...) { {...} else }.
8:00, I don't know about C, but in C++ is undefined behaviour to use an unitilized variable, like 'key'. Fortunately, there's a compile warning for that.
16:31, a possible mistake, even after this "fix", would happen if there's a small margin of difference between both sensors. Btw, it's likely, since sensors use to be analogical!
1
-
1
-
7:36, I'm huge fan of macro. I think it even should be expanded, being more powerful. But of course, if something can be made without a macro, go for it. For C++, typedef (nowadays "using") provide alias for types, lambda works as private f() within a f() (something that I want for long). All of that work as encapsulaters for technical stuff, letting us to think the code in a more high level way. Macro can do this, even joining pieces of code that wouldn't make sense otherwise.
For those that don't like the plethora of things like that that I write, I just say: their definitions are always either in a) Right above the current f(), b) Above the current file, along with its global constants, c) In a global place of the project files. That will depend on the broad of its meaning.
On IDEs like Codeblocks, just leave the mouse over the alias/macro, and it'll show its meaning. Or right-click over it -> Go to its declaration, after a recommended Ctrl-B, to mark the current place to go back later, with Alt-PgDowns, if the declaration is in the same file.
1
-
29:24, I agree, but I would never rewrite STL because of that. My f()s and classes use to not be generic, whenever possible. For things coming from STL, I use typedefs.
30:15, this is a Java thing. Well coded C++ use friend keyword, to allow just a few f()s to access nonpublic data.
31:43, std::stringstream is just a higher level scanf. And faster, according to a measure I took. I would only argue against it if performance was at stake on it: pointer, for instance, is much faster. It's syntax is not ugly to me. And std::to_string does the trick, if this is the only reason of using this stream.
32:25, 1 line f() could fit inside the class definition. And using std::clog, to not be generic, would dismiss receiving ostream and another class too. Result:
auto show_val ( ) {return std::clog << val;}
Plus, even using the overload, it could be made via:
std::clog << "blablabla" << object.get_val();
I think the code in the video is beautiful, if 1 desires what it offers: throw strictly the object (not 1 of its members) to an object that is or inherited a std::ostream. What is stupid and ugly is to write a f() like that (in the video), when 1 would be satisfied with the already existing 'std::ostream::operator << (int)'.
And printf is better only when several values are being read at once - otherwise it's less productive, due to type specifying ( + warnings) and demanding more typing on keyboard. So their thesis of condemning streams fell flat.
35:25, I have a tolerance about 1 line f() definition (below its header) inside class definition, because I can still put { } on the same line. For more than this, if { } is used normally, it starts to push the code downwards, looking noisy to me.
I also try to align returns, f() names, 'this' specifier, and f() definition, whenever they are "attached" (1 right below the other). Of course, I don't put these many stupid unnecessary spaces between the "fields". (35:39, giggles)
About the horizontal look, it's ideal for eyes, since they are widescreen. Code is ideally meant to be looked by eyes, not with help of hands, unnecessarily travelling vertically.
1
-
1
-
1
-
1
-
1
-
1
-
36:20, I think dismissing 'get', just 'length', is much better because it works accordingly to the Polymorphic Principle: "1 interface, multiple methods". The object (the "interface") is giving the context, so length refers to it (as well as the other f()s/"methods") - its size, in this context. Code is also assuming a default here: whenever the action is not specified, it means just the word (it's value, probably). I believe assuming defaults is a good idea, taking advantage of typing less, speeding up communications, saving energy, and so on.
Btw, 'size' is a much better name, because it's shorter, more broadly used (by STL, at least) than 'length', which confuses foreigners about th vs ht.
The only downfall for a direct name is that 1 probably has a private variable with the same name. Some people use a '_' suffix, which is kind acceptable, but not a tradition yet. So the solution may look a bit annoying.
38:09, good thing showing the footnotes. You should do this in all videos.
41:13, no, damn it! It's helping you to fix the BS you were about to do!
PS: Diablo 1 has the better source code I saw from games. It avoids vertical code most of the time, it's simple at all. However, I spot 2 flaws: literal numbers and other that I don't remember right now.
1
-
1
-
19:05, I forgot to mention 1 more thing: I would make person a class, instead of a struct. Compiler would fetch me a list (as errors) of all f()s who interact with the class.
Then I would copy their signatures, putting them inside the public area of the class, as 'friend's. Now, whenever some error happens due to wrong value for any member of that class, anyone would know EXACTLY where to look 1st. Awesome feature! It'd also avoid to change member data accidentally, on other unauthorized f(). Whenever that happens, the programmer would be invited to rethink the design. In my experience, this results in better design and less bugs.
To avoid make unnecessary friends, f()s who only read fields from the class, would not be friend. They would make the read via public getters I would write - but no setters.
1
-
1
-
1:28, to avoid forget to close (, {, [, I kept the option of automatic closing them, right after I opened them. The same for return instruction: whenever I start writing the f() header, I put the return right away. Other solution is to declare the returning type (at left of the header) as auto.
1:30, I only use C-array when it's const an already initialized with values, that I'll access through enums. At this case only, it provides advantage over C++'s, due to shorter notation. Otherwise, I always use this last 1.
2:11, this includes building defensive tools, to make it safer, far from its default.
3:20, I'm learning to use MQL5 for finance, and they use a C++ inspired language, more defensive by default. I also heard about some people using Java, to make often changes with less risk. But I heard too, in a presentation, that "C++ is the language of choice on this subject" .
5:50, once 1 get used to those, they become easy to manage. If bug happens, it's no more hard to find.
8:53, I heard C++ is starting to replace C on there too.
11:47, since I don't like to configure compilers to attach them to code editors, I only recently got the complete C++17. In Linux, I finally get C++20. Android is barely at C++14, at least with SDL dialog to Java JNI.
The good news is that, to become massively more productive, just C++11 is needed, and, for a few blasting features, C++14. C++17 (and I guess C++23 too) is weak, but for C++20 it's said that it's the new "changed our way of coding" .
12:57, it abstracts the low level by default, it's middle-level, and can jump easily to high level, if 1 develops his own classes, working exactly the way he wants and the project demands.
13:03, all of this from C++11. Lambdas are nice: I just type [ ( { (, and end up with [ ]( ){ }( ), completed by the IDE, which is the hard part. Or I can just type lbd + Crtl-J, Codeblocks will expand this according to an abbreviation I previously wrote, that could be like that 1 in the video. To avoid conflicts vs the capture, just type [&], and it'll capture everything as a mutable reference, also dismissing having to receive f() arguments.
1
-
14:08, there's a way to get rid of all those if checks, safe and easy that even C can handle: get a file only to handle the nodes chain/tree. There, some private content will handle the control over the nodes. For public access, only public setters. (This is 1 of the cases this FP approach can enjoy the same safety level from OO. It only evens because those setters are supposed to be called from anywhere) . So the use would be like:
create_node(); // Let's assume it failed. Optionally, an err msg could go to some log output.
goto_next_node(); // It automatically checks the next 1's validity, thus doing nothing. Another err msg to log.
int a = read_var_A();
// Validity check is made here too. Since there wasn't a "next node", it'd return variable from the current 1. But since the list is empty (automatically checked too), a literal value would return. Log should report all of this.
goto_previous_node(); // There's none, so it'd not go anywhere.
So, this is pretty safe. Log could even get better, by using a trick I saw in a Eskil Steenberg's video: each of these f()s could be a macro call to their actual versions. i.e.:
create_node_ (__FILE__, __LINE__); // It'd call this behind the scene.
By reporting the current (at the calling moment) _FILE_ and _LINE_ to log, it'd not matter how large the project would be, the exact location of the error or missing explicit check by the user would instantly be known, making debugging tremendously easy.
The price is that, despite the code would be clean of explicit checkings, the generated bytecode would has lots of implicit checkings, leading to much more branches, thus slower code. But this is easily fixed: once the app is safe, follow log's instructions about locations, adding explicit ones. Once no more of those err msgs appear, just change 1 macro line, which controls whether or not the implicit checkings are compiled. And then recompile the whole thing.
1
-
1
-
My way of doing things is 2h for tactical work (coding in computer), and 1h of strategic work, away from computer. Quality software is done by both of these things. 4:11, if "perfection" is taken as some previous broad plan (strategy), it rarely will be perfect. Tactic is needed to give feedback. The same way, nobody should just code, because a good plan can save us from getting obscure paths.
3:15, a meeting to schedule things is a complete idiotic idea. The reason is obvious: there's no deadline in software. However, meetings to configure/update ways of how a team should work, establishing standards, rules and overall strategies is something that should be done, even often. 3:47, don't schedule, schedule is mistake. I can prioritize things (despite not ideal for me), but I'll finish them when the time comes, period. No schedule, no deadline.
5:14, even not having distraction by phone nor other big noticeable things, often appears some small/tiny things, like thoughts of jokes, emotions, interesting ideas (not related to the work), memories of something you can't miss (and have an idea to enforce it right now), and so on. So, the right solution for this is timed work. And if I'm completely honest, stopping the timer whenever these things come along (or I go to the bathroom), my 2h becomes 3:30, in which 1h was spent for strategic approach (paused timer).
6:41, but I'm pitching it. 4h is ideal. And by that I mean 2h timed, with honesty. I hate 8h, I can't take this any longer! This lets me with plenty of energy for doing things free of charge, like a meeting (demanding useful ones), writing a report (mostly from the current day), discuss some ideas (strategy in an amplified view). Of course, not as an obligation, but there's a tolerance and a good sense for these extras.
1
-
1
-
1
-
6:33, hehe, Codeblocks is like that: a mid-term, not as fast as Vim, hasn't all its vertical moves, nor all InteliJ features. But I like to think it has the best from both: it's fast and light enough, doesn't require previous study, just install and run, opens in some seconds (ok for me), doesn't erase things I wrote, has plenty of IDE famous features, and I can also edit them. I move fast on it. It's what really matters to me.
1
-
1
-
1
-
1
-
5:56, throughout projects I work, I use to have "known issues/new ideas/refactoring tasks/reports docs", even in intermediate releasable states. I'm used to test every change, but once detected, I don't necessarily solve the bug, taking note instead.
15:10, what is this?
16:44, unless the coder stops everything to catch the bug, I think is better take note from the bug. Plus, there could be 2 or more bugs detected roughly at the same time, or the 2nd bug araising before the 1st is cleaned.
1
-
1
-
4:13, this is strange. If it's maintainable, it means above all things that it hasn't UB (thanks mostly to the high level language you are using) and that it's not changing variables on wrong places as well right places too, in different f()s (well structured).
But if it's good on this, it should be prone to be readable too. If I would risk a guess, I would say some variables are been changing on wrong places only, damaging the meaning of things (strategic view). 5:43, this may endorse what I'm saying: the goals of each f() were properly defined: they are mixed. Some f()s are doing job of other, when they shouldn't. 6:05, my "giant" f()s use to be 5 screen sized. They have a preparation of data (that only makes sense if used internally) to be used later, so this takes space. Strategically, it's easy to see: preparation 1st (2-3 screens, let's say), a main loop later, processing them. I keep things inside the f() due to encapsulation: I don't want the rest of the project having direct access to that functionality, since it would not make sense.
But if the f() was long enough, making me start to forget what was done earlier, even strategically, then I would start chopping it. I would make a class, having the f() as public, and several other f()s private, as its internal content. No way I would let it with 200 lines, if I would start to loose understanding over it.
6:23, the problem with FP is that it's too optimistic about its safety. For example, to work with multimedia I like to use SDL2. It's pure FP. So I get some of those things, that no alien should mess with, and I put in classes. So I think in case of confrontation, OO should force FP to adapt, because it brings issues to code safety.
1
-
1
-
3:15, I can't, there's C++.
7:25, it's better to separate resource/muiltimidia from the logic the commands them. Because resources can be large, staying on the RAM/heap memory, while the logic goes to CPU caches, becoming much faster. I use to let a class hold images, for instance. Whenever wants to draw, it's send a number/code, meaning a request for some specific image. This also avoids unnecessary duplicating resources, since they will stay in a single instance class. I also avoid making unnecessary classes. I only create them to defend critic data, since this is already built in the C++ core.
8:30, the same thing is applied to situations where an object is about to be unnecessarily duplicated: keep things in 1 object, which should receive request to access/modify its content.
15:00, these are not good examples for classes, they could be just f()s. Drawing and saving are actions, classes are mostly for data. Class is recommended for when data must travel safely along the battlefield. Safely means only changeble by authorized f()s.
1
-
1
-
1
-
The likelihood remains 50%. However, it's still interesting to switch, taking into account 2 scenarios:
a) Switch and loose: whatever he chose would result in no clues at all.
b) Switch and win: he had to choose the exactly door, 50%, or 1 in 99 doors, 1 / 99 chance. It's something, more than nothing.
Switching would be like flipping a coin twice, with the condition of:
1) If the 1st side appear, the 2nd flipping would be normal, 50%.
2) If the other side appear, the coin would be rigged to the person's side.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
10:54, I agree about the spaces, because they actually aid on visualization. But it's better to not put { } whenever possible, due to "clean code": less typing, faster compilation, saves energy, visually better. The only drawback came with macros (C++ example):
#define my_pairXY(fA, fB) int x = fA(); int y = fB() // Dumb code. It should be int x = fA(), y = fB()
for (blablabla) my_pairXY (getX, getY);
And then y slipped away from the for! But everything has a price to pay. By my experience, it's worth.
12:29, that's my dream. I'm forced to be C++ lawyer, because modern languages are too dumb to take its several good ideas and improve over it. So, with that mindset, nothing better seems to be appearing for the next... decades? I guess I'll take a deep look into Lisp.
13:07, probably means 'obese'.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
6:00, yeah, choosing (what's likely to be) a correct data structure should be the starting point. And this is 1 of contiguous memory, like std::vector in C++. Because a mistake here will take too much refactoring later. On the other hand, algorithm is something that, even if is entirely wrong, 1 will has to change mostly inside 1 f(), not scattered parts throughout the project.
9:05, OO is so flexible, that 1 can inherit that class, and implement his version of some functionalities, not changing it.
10:29, I guess "Dave's Garage" (ex-MS) channel revealed who the guy was, in his series about this algorithm.
11:11, precisely. I made a small game that fit entirely in L1 cache (64 KB for the logic, not resources), after compressed in bits several variables. I spend less than 10% of developing time optimizing it, which led it to 30x faster. And even so, I wrote classes and abstractions (not interfaces) all over the place. It's maintainable, easy to catch bugs, even after not seen it in a while. And it's readable, due to enough abstractions.
25:20, there's a quote which says "Who works with a hammer tends to see the world as a nail" . He wants to say "use the right tool for the right job" .
1
-
1
-
1:49, even when I used to code in C it didn't crash all the time. But C++ is the right solution. There are so many possible abstractions tools to develop using it, that you can fight back the dangerous from C. The major issue is still the pletora of UBs.
4:18, C++ allows to std::move a variable, but you have to do it explicitly (by copy is default). And if you use the moved 1, the app becomes unstable. Fortunately, there are external tools that catch this kind of bug.
8:00, D has an optional GC, while C/C++ rely on external tools to solve this. The memory issue is kind of solved nowadays.
9:50, this is as old as C, as reference is implemented as a pointer. It should not even be in your list.
10:00, does C has now templates? It really needs it. But I guess you are thinking about C++. Templates are safer than macros, but also less powerful. What Rust innovated was hygienic macros: a bit less powerful than C/C++, but more safe. 10:09, C++ has template metaprogramming which is pretty nice. The compiler follows you, saying "Hey, you mistook here". At least in C++ is safe. C still depends on macros.
11:04, no classes == no clothes.
1
-
1
-
1
-
0:01, I'm using Linux Mint 21.3 right now, watching this video. 0:10, and Arch is a s##, btw.
6:18, you should launch a course about making MangoHUD work on Linux Mint, for both 32 and 64 bits apps. I took a week to achieve it!
7:00, I use to omit the sudo for Linux cmds, get the error, and only then I write it. This is meant to get used to behave in a safer way. Like classes that I write in C++: everything is private by default. I let data like that (even knowing it won't work), get compile errors, and only then I either put each as public (rarely), when time comes, or give the "invasive" f() a VIP card to access it, if it is actually 1 of the few deserving it.
7:35, PassWorD. Stupid name!
9:46, I avoid using terminal: writing is prone to errors, more demanding of energy and memorization. I use right mouse button->proprieties and click on them, whenever possible. Flatseal is a flatpack app to manage permissions, at a more higher level. I recommend it.
On the same vibe, when coding I use code completion or even Ctrl-C + Ctrl-V. Anything to avoid writing the whole thing.
10:37, how to kill or dodge 1 of these processes, when it got the fullscreen (out of a terminal focus), and Alt-Tab doesn't work? And when Ctrl-Alt-Del / Backspace only throw the user to the "end of section"? How to continue the "section" on desktop or another app?
11:06, Mint is not that kid's stuff. I'm currently with Lutris not working and, for Heroic: Space Ace gets black screen when hit the play button, Trine doesn't want to launch, and Dragon Age - Inquisition doesn't even install!
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
5:38, as long as I remember, this behaviour is just a default. You will have to consent access to change variables, otherwise it would be extremely limited. And it's at this point where bugs arise.
7:17, C/C++ have the idea of advancing in block, covering "all" about some subject or not advancing at all. It's all or nothing. C is not much more than a portable, multiplatform kind of middle-level assembly language. A hashmap is a whole software building, focusing on a specif purpose. So, or C will has "all" these kind of high-level software structs (at least the most common ones), or it won't has not even 1 of them. Being an extremely shrinked middle-level language, those things would sound too high level.
Although I respect this kind of economic thinking, in my opinion C++ seems to has a much better tradeoff, despite still being an "all or nothing" language. For instance, it embraced those far too known classes.
7:38, (giggles). I can understand that. But I think it would be worst if not using C++. I mean, when 1 blew his whole leg off, he were facing dragons, not bugs, because these use to be killed for breakfast.
8:00, I heard that Rust has a focus on memory safety. This is pretty handy, but it's a focus that made the language loose ≃30% speed.
C++ won't handle that, unless its "all or nothing" philosophy decides to throw itself on defending all kinds of well known areas: memory, types, common mistakes, and so on. There's a set of optional slow runtime diagnosing tools, called RTTI. But the best thing is that, unless I'm wrong, the language is the best for making tools, and those user-defined tools can have a focus on defensive strategies.
9:52, nowadays, Fortran is translated to C++. It used to be faster in the past.
10:55, Pascal had the idea to tie the beginner to good practices.
1
-
1
-
1
-
1
-
1
-
0:59, no fun? It's awesome!
10:33, I watched a presentation in which some dude said his team failed badly with Haskell. And if we add to that the Haskell motto, "Fail at all cost!" , we can assume many had tried... and failed badly!
I can imagine why: there's an obsession about pure f()s. The real world needs don't embrace that, most of the time. So 1 has to update variables outside the already precarious encapsulation of a f(), to fulfill the side-effect needs, so avoided by the language. Thus, everything becomes periculous. The disaster is just a matter of time.
12:50, what does that mean? Once you used htmx, you just use it, without an opinion? The perfect pragmatic tool?
1
-
@digitalspecter If variables should not be created, everything could be pure f()s. But there are reasons why things once obtained should be memorized: either performance (expensive to get them again), tied 1 to another (need to be changed together), and so on.
Depending on the project complexity, there's not much space for pure f()s. i.e. if you turn the head of character in a videogame, changing his angle of view, even if not changing anything in the game, variables x,y,z need to change in some place, and memorized, because they'll be asked later.
Now imagine if you split the f(), which calculate changes, to make it pure, letting the variables attribution for another place. So we have the calculating and the changing f()s:
Pros:
- Code become more conceptual, where more actions have a name.
- 1 can shine in Haskell community, by making more elegant code.
Cons:
- Looses encapsulation: things that could better fit in 1 f() are now scattered in 2 or more f()s.
- Harder to debug: more places to look. It's needed to look the production of the value (pure f()) and also the attribution. They should be 1.
- Even worse: the rest of the project can now see and call those new f()s (btw, C++ allows the user to forbid that) .
I'm not advocating to have less f()s. But create them just to feed the Haskell utopian idea to has a "pure project" is bad, dangerous, even prone to disaster!
1
-
1
-
3:20, it's political, because the language is being condemned by things it almost got rid of. So he's saying that they should implement those safety measures as frameworks, instead of the already existing external tools. Because otherwise people go to Rust and alikes, and once other codebases grow with those languages, there won't be place for C++, outside its giant legacy codebases. It'll be the new COBOL! An undeserved destiny for the best language!
8:07, it looks contradictory: the language always was made unsafe (for performance) and always aimed safety, as a long term goal. It has a conservative way to see things: it wants to evolve, without sacrificing things on the way. I guess that vision is correct, for its ambitious goals. And having this in mind, the language has indeed evolved quite beautifully, in my opinion.
10:22, that's because he was wrong. People are too enthusiastic over Rust, at the point of misjudging C++.
11:31, Clang compiler launched a 'modernize' tool, that rewrites old working code to new standards.
1
-
1
-
22:36, C++11 also had for range loops: for (myType &value: vector_of_type). But I kept using the old for, through a macro like: myfor (it, vector_of_type), because I felt counter-productive to have to specify the type used by the container. I only embraced for range loops in C++14: for (auto &value: vector_of_type), using auto to kill that faulty feature.
25:02, I disagree, because these things are keeping themselves generic enough to work with any type. And everything is separated by scopes, that's why so many :: operators. C++ even has a kind of dry syntax, compared to how many things it's handling on those libs. 1 has to compare beauty to what it's trying to achieve.
27:00, macro can clean this code. While the lib has it in its generic form, to hold all possible user configs (and I think it's beautiful, because it achieves that, with likely minimal syntax) , the user don't need to do that. If it happens to has several arguments, just make a macro or typedef about its meaning:
#define ParamsForHashDecl typename KeyType, typename ValType, // ... the rest.
#define ParamsForHash KeyType, ValType, // ... all the rest.
using ProjectsOnlyHash = HashTable <ParamsForHash>; // Alias.
Since this is made only 1x, similar f()s headers would be like:
template <ParamsForHashDecl>
const ValType &ProjectsOnlyHash::getValue (const KeyType key) const;
getValue is a function from the class 'ProjectsOnlyHash' (1 doesn't even need to read its template args) , that receives a KeyType (that can't be changed) and returns a reference of some ValType, which can't be changed either. The f() also can't modify data from the ProjectsOnlyHash class, except those declared as 'mutable'.
At any time throughout the project, if the user wants to remember what is ProjectsOnlyHash, or its template parameters, just leave the mouse over the respective word.
1
-
1
-
1
-
1
-
1:55, despite having a map class, C++ allows to do this code way faster and consuming less memory.
2:03, is there no ternary operator in this language? C++:
auto normalizedFavScore = faverL2NormOfDest > 0 ? inputEdge.favWeight / faverL2NorOfDest : 0.0;
2:16, I hope those literal numbers are only seen here. Otherwise, they should be global constants. In C++ you can separate digits: 16'384, 1'000, 175'200.
2:34, if all those strings were integer constants, it would be way faster for seeking a value later, for instance.
1
-
1
-
1
-
1
-
1:00, I'm the minimalist:
- I have a flip phone too.
- Won't put an e-lock in any door, neither buy a car that takes pictures of me each 5 minutes, to send to the web.
- I use Linux.
- 32 bits videogame generation was the best, even for playing nowadays.
I also believe in minimalist job (2:22):
- Timed job.
- Don't care about the working place.
- Engaging even in the worst activity, like debugging.
- No overtime.
- No deadline.
2:09, bro, write in C, duh:
long isEven (const long n) {
assert (n < 63); //Call a looser to continue this, in case of failing.
return (1L << n) & 0x5555555555555555; //All evens have 1; odds, 0.
} //Bro, I don't know how many % of n has in 2. Duh!
1
-
1
-
14:09, this is good, not an issue, because it'd result in less refactoring, if changed, leading to more productivity.
14:20, 1st of all, this isn't real-life example. std::vector is a pretty fast data structure, that auto-manages its size internally. The compiler is so smart in optimizing it, that I often see it rivaling fixed size arrays.
14:47, I use msg or msn, to keep same letters as in portuguese.
15:00, what are you using? Vim? (kidding) :goodvibes:. On modern IDEs, Codeblocks for instance:
a) 1 can just leave the mouse over the f() name call (cursor not required), and 1s later it shows the f() signature.
b) Right click (on f() name) -> Go to declaration: it opens its header file, right at the f() signature.
c) If nothing of that worked (mostly due to not filling a cache yet) , add an absurd parameter on the f() call, and recompile. Compiler will say: this is stupid, look the f() signature: it opens a small log window from below, from which you can already see its signature, or left click on it, to open its file.
d) Write a tool: got_message could say what it changes, whenever called:
#ifdef SAY_WHAT_YOU_FUNC_CHANGES_WHENEVER_USER_DEFINED_THIS_MACRO
printf ("got_message changes size");
#endif
Then, on a click of a finger, 1 can disable this on the entire project, by just changing 1 line and recompiling it.
16:07, true, but I think the team should agree on defaults, before even start the project.
1
-
0:13, C++11 is the "must see" 1. From that, only minor good features have been made. C++20 is now a game-changer, but it's more related to high level features than performance or middle-to-low level functionalities.
0:38, oh... really nice to know that.
2:51, this is what I believe C++ is able to achieve. But that's not its default behavior. 1 should go (sometimes struggle) for it.
7:43, maybe because of its complexity, it feels solid among the complexity of a project. I was making 1 in 2 steps, the 2nd "as a superset" of the 1st. I basically inherited all the 1st step as basic classes for the 2nd. It worked amazingly! Of course I had to amplify things, but no extra problem was added by that. And the classes were organized by data, not so much by meaning. Even so, the whole previous meaning worked for the 2nd, and no performance penalty was added, beyond what was inevitable from more work to do.
9:40, delete all of them, letting the compiler pointing you the missing ones.
1
-
1
-
2:48, they just gave a name for a thing that has been made in C for decades:
struct CheeseBurger { const char *ing_1, *ing_2, *ing_3 };
#define createCheeseBurger CheeseBurger { "bun", "cheese", "beef-patty" }
And then call it by:
CheeseBurger a_cheese_burger = createCheeseBurger;
5:20, this is a "lesser version" of C++ for 2 reasons:
a) If 1 just wants to customize the initialization, C/C++ way is much better, as exposed above, due to compact syntax.
b) If the goal is to customize it along the way, not only on initialization, it should has a control of which f() is allowed do that - and it seems that only C++ has this feature . Otherwise, all sorts of bugs can come from it, because everything will be public, which is the worst nightmare, depending on the project complexity.
10:57, why use an interface for that? Couldn't it be just a class? 16:00, again, why an interface? This thing is awkwardly slow!
21:00, and that's why f() programming < OO: the 1st lacks good facades. In C, you can let that memory management "encapsulated" away in a different file. But there are issues:
a) Each file like this will be dedicated to each object, which hurts scalability.
b) If only 1 generic file like this is created, then the user will has to keep traveling alongside with that array, which is exposed to dumb mistakes for long unnecessary time.
It's still possible to create several macros, trying to hide that array exposure, but it's still a precary solution.
1
-
1
-
1
-
1
-
1
-
1
-
+Tegan You can have 1 vector filled with "unordered" indexes from, let's say, pixels in screen, and the other (vector) with "orders" (string), and a lambda f(), to coordinate them.
So, you say '[]("HIT THE MAIN CHARACTER")' , and the lambda finds this string in 1 vector, measure the index from the beginning, then returns the match pixel, from the other vector.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1:59, ha! Codeblocks is faster on this 1! On error, it opens a small windows, hidden from bottom, then I just click on it, and I'm already on 45! No extra typing, no new screens. Then F2 to get rid of that screen. 2:11, that would be Ctrl-G (left hand) and 45 (right hand, on keypad), IF, and only if, I have to do that! :elbowcough:
2:54, FC? That was fast. I hold Ctrl-Right, and arrive on that in 1s I guess. Then right-click -> 'Find Implementation'.
3:41, for that, I would had left a Ctrl-B (bookmark) on there and here, then Alt + PgUp/PgDown, I go there and back.
3:55, I open those on tabs, then Alt + Right (it opens a menu with those), then Up/Down to navigate them, while still holding Alt. 4:14, but never at this speed.
6:29, faster on that too: just Ctrl-D, and it doubles the line. It doesn't even need to select it, just having the cursor on it.
8:16, I save often too, but never more than once per time.
8:20, 5-Down? That was nice. I guess C::B doesn't jump that way.
10:44, I hold Ctrl-Up, to roll the screen up, towards the cursor. 10:46, Shift-Down or Shift-End to select the whole line. The 1st gets the newline character too, but needs the cursor positioned at leftmost.
1
-
1
-
1
-
@plan.b2 If all you are dealing with is a database, which asks to the user what action to take, does that, and asks again, it's fine to use FP. But if you are handling a highly interactive project, such as a game, operating system or multimedia at all, things can get really dangerous in FP. Let's suppose the user starts frantically kidding with window resizing. This will try to access many data, before concluding some tasks, testing the stability of the system. OO has much more chances to catch any error, due to its restrictions. For instance, you have control of who is trying to reach the hidden data.
Here's an example of that: https://www.youtube.com/watch?v=0Yh80SvW9mo
1
-
1
-
1
-
1
-
1
-
1
-
8:35, I agree. But I don't know if debugger is overkill. I use to make unity tests. Maybe some prints. This solves +95% of everything. If bug persists, which is rare, I also use a technique I created, called "hacking the solution": I change the code a little bit, test, see results. Then put things back, repeating the process in a different way. This puzzle points me to the right direction.
10:02, I do that too. I think TDD is a bit invasive, when I'm developing the f() signature: I still don't know exactly what it should receive/return, so I want a bit of a freedom. As soon as this is established, I write the tests. Once both are made, the rest of the f() development/fix can reach a pretty fast speed, as it becomes oriented by the tests.
10:10, but I never delete test, unless it can be replaced by 1 that tests what it intended to, in a more edge case way. C/C++ also allow conditional compilations, including or not the tests. So their presence can be configured by changing just 1 line. 17:27, the same thing happens with all those asserts: if #define NDEBUG 1x before, all of them suddenly disappear. So the programmer is not condemned to their presence.
17:53, and compilers evolved too. I saw more than once std::vector (variable length size array) being faster than a fixed size 1!
19:30, it's possible to write tests that just emit reports/logs, showing errors, but not shutting down things.
20:18, I'm too. But I'm 2 workplaces on Linux because, when in development environment, I don't want other minimized windows annoying me, from the rest workplace. I also use the Cube, to give a nice effect when switching between them.
24:40, 1 of the reasons why I use Codeblocks IDE is that because, either on Windows or Linux, I just install (1-2 minutes), pass my pre-configured archive (some minutes maximum), and I'm already coding, with everything I want.
1
-
1
-
1
-
1
-
1:10, does anybody can explain to me what's the logic in calling enum a "sum type"? What's a sum type btw? 1:26, isn't std::tuple enough? 1:53, it's more comfortable than structs, due to possible accessing members via indexation. But I don't use it, because it generates too much assembly code, which leads to slower code.
2:00, are you sure? I know that acquiring/freeing resource are, and this is the main (and should be the only) goal for smart pointers. But raw pointers/iterators are pretty fast for memory accessing. All STL algorithms demand to use them (a few times as references).
4:58, but I'm, and I say it's the best language for crafting tools (more functionality/freedom). Whenever I face something risky around the corner, I build a tool to deal with that.
6:50, you can put declarations and definitions on the same header. I do this for small projects.
7:25, there's no issue about the private data being on the header file. It'll continue being forbidden for public access, unless otherwise expressed.
8:55, I rarely forget to type const. But I agree that const by default is useful. However, it's possible to create a convention for the team:
1) Create some prefix/suffix meaning const for the type, like 'int_'. 2) Do the same for 'mut'. 3) Config. to highlight them in the code editor. 4) Config. to NOT highlight the conventional types in the code editor. This way, the team will notice, at once, that something is wrong when they type 'int', and it doesn't highlight.
10:20, I think const_cast is always a mistake. There's an optimization the compiler does, by exchanging all const by literals, since the beginning, which might colide to that. Better is to go right away to the f(), fixing its const-less issue.
1
-
1
-
10:45, I guess the maintainers just opted for keeping the non-const iterator because it's a clue, of what the iterator will do inside the f().
11:53, "Move was a mistake. It should not be standardized. The compiler can see the allocators" - said a compiler maintainer. It can manage the movable things for us.
12:20, std::function is not mandatory. I would never use a runtime exception just to have it. 1 can use pointer to f(). Its only disadvantage over that class is its kind of annoying type syntax declaration, that can get worse. However, there are some turnarounds:
a) auto function = my_func_name; // By omitting the ( ), it's already a pointer to f() .
b) If receiving it in a template f(), doesn't even need to know what the type is: just pass the f() name, when calling the template f().
c) If it's not a template, and needs to know the type to declare it, just type another thing, not convertible: the compiler will halt, saying the type it deducted. Then copy/paste it right from the log.
12:42, as I said before, write a tool. For instance, why not put a printf on copy constructor? It'd warn you.
And non-primitive types can be send by copy. 1) Compiler may arrange things for you. 2) The size is more relevant than the type. For instance, I once had a 1-byte class. It had its f()s, but only 1-byte data. Passing it by copy on the entire project wasn't slower.
13:25, I don't think these are valid issues. It's just a call to a f(), when returning. If that's a concern, 1 can keep those values in a struct, dismissing calling f()s at returning. These "out parameters" may have a performance cost.
1
-
1
-
1
-
1
-
1
-
1:36, I think that would better fit as definition of productivity. I think maintainability is more related to not mess what was already done, whenever you need to change part of it, either by making new feature, refactoring, upgrading, optimizing, exchanging strategy, and so on.
1:53, I don't have this issue. I may forget a bit about how things communicate between themselves, but I get the idea from some minutes to some hours. I may also change the style a little bit.
What's the secret for this? I don't know exactly, but I keep the code commented in every tiny thing suspected to cause problems in the future. And I keep documentation about the project as a whole, outside the code too, for both strategy and tactic approaches.
3:35, this shouldn't be happening. Despite it's impossible to know how the entire project works tactically (the exact variables and how they change) , it's viable to have both the strategy (how it works at all, high level) of overall data structs, how they communicate and their goals; and the tactics inside each f(). For this last 1, I use to write its goal, comments in almost every line and even 1 or 2 edge examples, in case of a complex f(). I discover this need by making a brief "brainstorm" about it, mainly focusing on the future readability.
This is 1 of the key reasons why I have a "spaghetti" style of horizontal code, putting comments at its right side. People take this as ugly and unreadable at 1st glance. But I think it's compact (most of my f()s can be seen in 1 screen) , encapsulated (no unnecessary extra f()s are made, just for the sake of been "readable") , and commented enough for the future, without pushing the code downwards (which I think it's awful and damages readability) .
1
-
Dump C++ for memory safety is retarded, nowadays. Its tools catch all errors Rust does, without the compilation overhead.
3:20, that's the very reason why the language should not lose terrain. It gives us more freedom on how to do things. The #1 feature, above speed. 3:24, and at the same time have a high level language, with just a bit of effort.
4:38, Go is twice slower, and Rust seems to bring some stupid ideas, like not having classes nor inheritance (composition only). Is it right?
5:30, hm. I guess we should rewrite all Rust and Go in C++.
5:41, this is not enough. C++ doesn't deserve to be the new COBOL (alive due to its large codebases only). C++ is objectively better!
5:53, but Java being replaced by Rust... that's a good idea!
1
-
4:55, I don't use to have much problems with types. In games, they use to be way different from each other. For instance, if you mem. some data in a class, it will sooner than later be packed into bits. If you put some other object, when that 1 was expected, a compiler error will arise.
6:47, games are too wild to not use OO. They require proper encapsulation, in a level that FP can't provide.
13:34, I think a better design is to have several containers, 1 for each type. So 1 could has 1 for the white-hat guys, other for black-hat dudes, other for things, and so on. And those holding just data control, small stuff on stack, 100x faster. Only for multimedia, really large data, those smart-pointers would apply.
So another container on the stacks holds inf. for composing the scene: the index and container of things present in a certain moment. So data for other containers will be reached via indexes, avoiding the pointer invalidation problem - and also being much faster, since most data were computed on the stack.
14:17, sure. But doing this it'll be horrendously slow. The idea is to avoid the reallocation, for at least 90% of the time. So std::vector::reserve preallocates enough memory for that.
24:44, I heard that zealling feelings attract them.
26:18, it's possible to create a macro for both - so that both are changed together.
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
1
-
0:10, ooh, this is awkward! The right way is to have a variable in a class, and just ask for it, not loosing performance nor getting complicated, trying to guess what it is. It's even better if, instead of a variable, this constant is given as a template argument, by a f().
3:49, this is a long discussion, but I think quite the opposite. Horizontal code means 1 will spend less time traveling throughout the code vertically, which spends more energy, and looses a bit of focus. Plus, eyes are widescreen, they were made to see things horizontally. And also reading code is an eye thing, not a hands 1. I use tab = 2, to put more things on the same line.
1
-
1
-
0:08, hm... I don't know. I use to fly with C++. Unless Rust dismisses tests, which is not true, I have some doubts...
C++ can be felt as slower when typing declarations vs languages that dismiss them, or when those language have an already existing lib/algo to solve things at once. These are the 2 most common cases I can remember now. Other than that, C++ dismisses writing defensive stuff (this should not take too long) and has a concise syntax, that can be even more using macros. Running the app with some inputs known to have certain results, it soon becomes clear if a mistake appears. So stop and fix it. For experts, just by the looks of the bug (from the report), there's ~90% chances to be pointed to the right track of it - yes, almost all bugs are pretty fast to catch, even with manual tests, let alone automatic ones.
3:03, agree. I use only FP, until I find that some variable would potentially create a mess, if changed on the wrong place. Only then I start to use class, to both protect and control that var. This is fast to type (not bloated with unnecessary classes) and easy to test (mostly f()s). And even if an environment of classes state needs to be tested, through macros (plus C++ concise syntax declarations) it's possible to compress wonderfully an entire situation (several classes) in 1 automated test, maybe in a single line, if lucky! And that conciseness encourages to write more tests.
1
-
1
-
1
-
1
-
8:56, true. I think my spaghetti code is clean, readable and kind beautiful. But it's just me.
11:53, performance uses to fight vs all others. So if a code got kind ugly, on an apparent necessary extent to deal with performance, I don't take it as ugly. I think a code should be judged having its goals in mind. Example: Rust or C++ specifiers, to get data fit into safety constraints, is not ugly.
17:20, I think they are bad when they expose content that should be "private" of a bigger f(). Otherwise, I think it's good to not pass 1 screen. Spaghetti style helps with that.
18:14, using IDEs, just leave mouse over the abstraction, and it shows what the abstraction means, how it was declared at least (enough for macros, lambdas, types).
21:13, well, if I had the chance to refactor it, I use to be satisfied for years.
22:59, spaghetti code is the right tool for the job. :elbowcough:
1
-
1
-
1
-
1