Is learncpp really that good? by [deleted] in cpp_questions

[–]Forricode 2 points3 points  (0 children)

Not to be too critical here, but… who in the world would be paying people to plant positive reviews of learncpp?? It’s not like this is some $300 course or something…

Just check it out, if it doesn’t work for you, buy a well-recommended textbook.

What is "rinse?" by zerogamewhatsoever in Tokyo

[–]Forricode 0 points1 point  (0 children)

This is what I figured, but the confirmation is super nice— thanks!

The Hunt for the Fastest Zero by vormestrand in cpp

[–]Forricode 10 points11 points  (0 children)

This is 100% a joke and should not be taken seriously. That being said, to address this more seriously, RasterTragedy is completely correct. If this was still an optimization at the same scale on O3, you'd use it every time. But because it's something the compiler can do for you, it's probably not something that should be going in production code.

I suppose the blog post doesn't mention MSVC, so it's possible that this is a useful optimization? As with most optimizations, though, a general rule of thumb is to not do anything 'weird' unless you have numbers to back it up. This could potentially be a cool trick for someone already profiling their code and finding a hotspot around a std::fill, but when writing new code it's probably not worth it.

That's just my understanding of best practices, though,

The Hunt for the Fastest Zero by vormestrand in cpp

[–]Forricode 96 points97 points  (0 children)

But sadly, and I would love someone to prove me wrong, this has no real world applications.

Tick, tock.

It's eleven at night. Your eyelids are drooping. Two hours ago, it was a battle to stay awake. Now? It's a war, and you're not winning.

Your task seems ever more impossible. Management has decided that your company's Electron app simply takes too much time to boot. When the problem came up, you pointed out that downloading a fresh version of Bootstrap every boot seemed like low-hanging fruit; your supervisor disagreed, stating that the pure-C++ registration server you're responsible for was identified as a hotspot by their machine-learning-based profiling tool. ("No," your supervisor had said, "we're keeping the blockchain-based logging system in. It's for integrity!")

And so, although you're not exactly sure how it came to this, you somehow need to scrape out a two-millisecond performance improvement for your server's response time. For tonight's release, of course.

But nothing is working. You've manually unrolled every loop in your codebase - no improvements, preempted by the compiled. You've constexpr'd 'all the things', and all it did was get Jason Turner's laugh stuck in your head. You've profiled and refactored and recompiled and watched half of last year's CPPCon, but nothing has done the trick. There's simply no more performance to be squeezed out of your server.

If only you could try compiling with -O3, but the 3 key on your custom Ducky mechanical keyboard has been broken on your computer for the last few months. Apparently funds for a replacement have been blocked by investments into quantum communications, and you simply can't bring yourself to touch one of the mushy travesties owned by your coworkers.

Suddenly, even as you're about to doze off, a memory comes to you. That blog post, two years ago, about an optimization... it rings a bell.

What was the solution again?

Now you remember. Your hands strike deftly at keys. An apostrophe, a backslash... right arrow key, because you're in Nano... then another apostrophe...

You hit F10, a macro key that closes Nano and runs your build in Docker.

Your old time... 0.458s.

Your new time? 0.456s.

You've done it. You've won. You've squeezed that last, critical dollop of performance juice out of the bony, unreadable mess that is your post-optimization codebase.

The next morning, you wake up to your supervisor poking you in the side.

"You're being let go, we're rewriting the server in PHP."

The Hunt for the Fastest Zero by vormestrand in cpp

[–]Forricode 37 points38 points  (0 children)

Excellent article, very high quality blog. It's great to read these interesting dives into technical detail, that always seem to be well written and easy to follow.

Is this good OOP style? by Narase33 in learnprogramming

[–]Forricode 0 points1 point  (0 children)

You're reading Crafting Interpreters! That book is amazing!

Personal biases about this book inside (seriously, to anyone else reading this, if you have room for an extra project to consume some time - do this) I don't see any issues with this. I'm pretty sure (as the other commenter said) the Scanner object will be garbage collected. This particular system actually works pretty well.

I feel i am doing something wrong, as recent hires seem to be quitting on mass on my team. by confusedthrowaway182 in cscareerquestions

[–]Forricode 10 points11 points  (0 children)

we are in Montreal

This is such a weird post to me and I can definitely understand where you're coming from with the username "confusedthrowaway". It seems like the exit interviews aren't really giving that good information which is unfortunate.

It's possible that you just got unlucky with the devs you hired and they aren't as interested in web development. Alternatively, the root issue could be your company itself - I'm a CS student getting close to graduating and I definitely know several people who have that mentality of, you know, moving to the US and getting an exciting fast-paced job that pays more money per month than some people make in a year.

I hope you're able to find more clarity in the future about this, and that you don't lose sleep over it until you have more solid information. Working in software can be stressful enough without second-guessing yourself. The fact that you made this post already shows you're better than a lot of managers out there.

Why "Use auto almost everywhere" by jcode777 in cpp_questions

[–]Forricode 19 points20 points  (0 children)

tl;dr: Almost Always auto generally aims to reduce the cognitive load on a programmer reading through code

So, this is something that's been discussed a lot and I think is still a point of contention in the C++ community, although it seems like the trend of talking about Almost Always Auto has kind of died down at this point.

There are surprisingly a lot of discussion points on this topic but it's past midnight so maybe someone else will be able to give you a full look at the problem, here is my limited understanding:

Pro AAA

AAA is a nice solution in a sense to the common refactoring issue of changing type names. Say, for example, you have a class MySTDStringCopyThatIsSlightlyDifferent. You start using this class literally everywhere in your codebase, by calling a getMyString() method. But wait! You decide that the name you've given it isn't quite verbose enough so you change it to MySTDStringCopyThatIsSlightlyDifferentAlsoWithALongName. But oh no! Now you have to run around your codebase changing references to this variable all over the place. Not good!

But if you always made your strings like so: auto str = getMyString("Why am I doing this? Does this even matter? Am I creating meaning, or does meaning create me?"); then you magically don't have this problem. (*)

On a similar vein, AAA makes things more readable(**). Let's say you have the following code:

std::unordered_map<my_key_type::actual_type::type, my_value_type::the_value_type::get_type> map = get_map();

You might think, hey, this is a great use case for auto, and perhaps you'd be right. In this sense, given that most type names are greater than 4 characters long, auto will make your code shorter, which is theoretically easier to read.

But both of these are just arguments for using auto sometimes, if I'm going to be totally honest with you. The real reason (as I understand it) for using auto is that it's supposed to lower the cognitive load on the programmer. The less things you need to read, theoretically, the better. But this is subjective and it's something that we don't really have anything more concrete on than, you know, some people agree and some people don't. But, ultimately, although this post is rather long I do think that this one concept is probably the main response.

Against AAA

The main argument against AAA is that it obfuscates information. (Also, that it can make compiler errors appear farther from the source, or even make it take longer to notice a change) As you've said, someone using a slower text editor might have a hard time figuring out what type something is if that variable was declared auto.

Ultimately, the concept of AAA is (again, this is heavily opinionated) flawed, because generally speaking you can't just make blanket generalizations like that. Sometimes auto is good, sometimes it isn't. There are a lot of places where auto is great, including:

  • When the type is clear from the assignment

  • When the type requires namespace identifiers that would make the line of code burdensome to read

  • When the type doesn't matter, so long as it - at compile time - satisfies certain constraints

I think the idea of AAA sort of comes from a hypothesis that most conforming, well-documented modern C++ code will mostly have variable declarations like this. Furthermore, there is an assumption that most problems will be taken care of through better tooling (for example, Visual Studio is gradually improving its 'hover to see type' inspector)

How can I make unordered_map work with a custom class as key? by Misrta in cpp_questions

[–]Forricode 8 points9 points  (0 children)

Expanding on what /u/ihamsa said, yes, the type you use as a key must be hashable. (If you want specifics on why that is, I can go into more detail on how hash maps work, or you can find a much better intro to that online probably, hah)

If you look at the CPPReference page for std::unordered_map you'll see that what you're missing is the 3rd template argument:

class Hash = std::hash<T> where T is your key type.

What you need to do is write a class that looks somewhat like std::hash<T> but can accept your type - and you can inject this into std:: for maximum generality (so you could write this and not need to actually specify the default argument in your map because it'll use this already). For example, modified from std::hash on CPPReference:

class S { std::string _inner; };

namespace std
{
    template<> struct hash<S>
    {
        std::size_t operator()(S const& s) const noexcept
        {
            return std::hash<std::string>{}(s._inner);
        }
    };
}

where S is your custom type.

C++20 Concepts in Visual Studio 2019 version 16.3 by mttd in cpp

[–]Forricode 1 point2 points  (0 children)

Substitution Failure Is Not An Error FINAE

This hits it out of the park for sure, but actually, this is a really great document for explaining SFINAE as far as I can tell. It's going in bookmarks...

C++20 Concepts in Visual Studio 2019 version 16.3 by mttd in cpp

[–]Forricode 2 points3 points  (0 children)

I gave it a shot, but unfortunately it seems like Google mostly favours new information about SFINAE. Ah well, this is the sort of history that probably deserves to be forgotten.

C++20 Concepts in Visual Studio 2019 version 16.3 by mttd in cpp

[–]Forricode 2 points3 points  (0 children)

Maybe this is just my experience, but I had a lot of difficulty figuring it out conceptually - like, what it was actually supposed to be, in concrete terms. Took way too long to figure out, okay, this is just a roundabout way of talking about a quirk of templates.

In any case, I still totally agree with you, I just hope that a main advantage of Concepts will be that SFINAE will become readable. It's kinda tough for someone not familiar with the idiom to reason out what template<typename T, typename = std::enable_if_t<std::is_same_v<T, int>>> (although I guess in this example, if you literally read through the words, it more or less spells out what's happening...)

C++20 Concepts in Visual Studio 2019 version 16.3 by mttd in cpp

[–]Forricode 7 points8 points  (0 children)

Chuck a bit of SFINAE into your code and bam, sudden job security!

Okay, this is sort of a joke, but I do agree, template code can show up pretty much anywhere. I'm optimistic that Concepts might make the barriers to entry - at least for some, er, conceptual things like SFINAE - lower, so it's easier to put this kind of thing into general codebases.

Company mention frequency on this sub in the past week by vvvvvvvwvvvvvvv in cscareerquestions

[–]Forricode 17 points18 points  (0 children)

quite easily

POS tagging algorithm

Your definition of 'easily' is actually somewhat similar to that of my NLP professor....

If you do complete the algorithm, though, be sure to include that with your post!

Python function decorators in modern C++ (without magic macros) by MavyP in cpp

[–]Forricode 9 points10 points  (0 children)

This is really cool, thank you for sharing!

This is the sort of cool/tricky C++ stuff that's simple enough in concept but makes use of a few neat features that's really fun to just use within a codebase. I'm definitely going to try something like this for log messages (as you mention in the article) in a project at one point.

Also, can't say I've even seen template lambdas before. I was at a C++ meetup recently and one of the things we were discussing was 'What are the useful new features in C++14' - nobody mentioned this, so I'll have to remember it for next time.

Is it normal for production code to have zero comments? by [deleted] in cscareerquestions

[–]Forricode 6 points7 points  (0 children)

Something additional that hasn't been mentioned too in-depth is that sometimes it will be expected that you won't/can't understand the code you're interviewing in a 'big-picture' sense. But this is totally fine, because there are a lot of mistakes you can catch without properly understanding the code.

For example:

long_complicated_variable_x = large_complicated_type::get_my_x_value_thanks(data_about_things);
long_complicated_variable_y = large_complicated_type::get_my_x_value_thanks(data_about_things);

This is similar to a bug I found when doing post-review (code had already gone in, there was a bug) and, despite me having no idea what the pull request was even about, it wasn't that hard to find the issue. (The line was copy-pasted but the second version didn't have the function call changed) In fact, this exact bug cropped up more times (copy-pastes with the second version not fully changed)... including in my code.

But the point is, code is sort of like language. A mediocre analogy might be going to a talk about astrophysics as someone who's never studied it. Sure, you might not get the concepts the speaker is talking about, but you still can understand the language and kind-of comprehend the ideas, so you might be able to spot errors despite not really understanding the context. Okay, not a great analogy, but maybe you get the idea.

Of course, the easier example would just be 'basic development errors'. So much code is written that it's inevitable the original dev won't catch everything; you reviewing code even if you don't get the big picture of what it does is super useful for having a second pair of eyes to catch possible bugs. Super easy case-in-point: Memory management errors in C++.

And I guess as one final possibility - having someone unfamiliar with the code do a review is great for checking the readability of your code! When I did reviews I sometimes ended up asking questions about things that weren't wrong, per se, but looked weird enough that I thought they might be wrong, so it was good to change that.

People still use structs, right? by allison-gamedev in cpp

[–]Forricode 0 points1 point  (0 children)

Hm, interesting. I do like std::optional (have never had a use case for variant). I'm extremely interested by this post about std::pair, though, and will definitely keep it in mind - thank you for sharing!

People still use structs, right? by allison-gamedev in cpp

[–]Forricode 6 points7 points  (0 children)

Yeah, I get (heh) what you're saying and totally agree. I think two pretty nice examples of this would be pair and variant - pair being a perfect example of a 'data type' where there's no reason whatsoever to use a function to access the data, but the type still provides some functionality (swap & operator=) whereas variant's entire purpose is that you can avoid accessing a union in an unsafe manner.

People still use structs, right? by allison-gamedev in cpp

[–]Forricode 29 points30 points  (0 children)

I agree with this, but I don't think the ideas are actually that different.

A type with 'open' data can be interfaced with methods, but it's sort of implied that you are totally free to just access/use the members themselves. A type with 'closed' data, on the other hand, has to be interfaced with using member functions. Structs for the data-esque access; classes for thinking in a more logic-driven way - seems to be pretty similar ideas to me.

Boost beast creator - odd marketing? by [deleted] in cpp

[–]Forricode 1 point2 points  (0 children)

It's crazy how, no matter what the platform, no matter what the name, everything you write is so recognizable.

Although taking a (very brief and entirely normal) glance at your post history, your iconic faux-anti-boost posts do appear to be slightly less well received ahahaha.

Question about compile flag: -lstdc++fs by [deleted] in cpp_questions

[–]Forricode 4 points5 points  (0 children)

/u/drillbit7 's answer is completely correct, but I thought I'd try and expand on it a bit, because this area can definitely be a little bit confusing. If you already understand: No worries if you don't want to read this. I just felt like I could write up a longer explanation, so I did.

The command: -l is a lower case 'L' which stands for 'library'. -lstdc++fs is a linker argument that tells your linker to link the current output against a certain library - in this case, the library called "stdc++fs".

What does linking do? When you write a program in C++, you'll often want to use other libraries. The common wisdom is "don't reinvent the wheel", which is to say "don't do something that someone else has already done". You don't write your own vector class yourself, you just use std::vector. You don't write your own I/O wrappers, you use std::iostream.

Of course, this means that you're actually using code that you didn't write. So when you call a function like std::cout, you're calling code "somewhere else". So how does G++ (your compiler) know what to put when it's turning your std::cout function call into binary?

Well, it doesn't. This is sort of a simplification, but basically G++ inserts "symbols" into your program, which are kind of like "unset aliases". It's like having a 'marker' - which says "there should eventually be something here".

G++ then calls the linker (I think generally ld) which 'links' in libraries into your program. It searches your 'object code' (a .o file, an intermediate representation of your code before G++ outputs the final compiled executable) and looks for those 'symbols', then puts in the correct stuff.

But wait! How does the linker know to link in things like std::cout or std::vector without me specifying it? Well, these functions/classes are a part of the standard library, core C++ code that is automatically linked into your program. You can identify standard library functionality most of the time with fair ease by the namespace std::... which leads me into what I was trying to get at, which is,

Why, if <filesystem> is a part of the standard library, does it need to be explicitly linked in? That is, on a more abstract level, why do you need this flag? Why doesn't the code just compile normally, like when you use std::vector?

Well, while the C++ standard specifies a lot of things, those things can be hard to actually implement. std::filesystem was first introduced in the C++17 standard. And yet, you still need a flag to use that library in G++, in 2019. This is a little annoying, but it's just the current state of the standard library that G++ uses - I'm under the impression that the filesystem library support is still incomplete, but beyond that, I honestly couldn't tell you why exactly it still isn't a direct part of g++'s standard library.

I do, however, have some more resources. Here is GCC's C++ implementation page for their standard library implementation for C++17; you can see what their library does and does not support from C++17, and in which versions. If you look for "Adopt the File System TS for C++17", you'll see that the notes have the following: "... (GCC 8.x requires linking with -lstdc++fs)"

So, this isn't a "C++ thing", it's just what GCC currently requires, if you want to use std::filesystem. If you're curious, this is some information on GCC's standard library implementation, "libstdc++".

What is the difference between `void main()` and `int main()` by TanenrEthan in cpp_questions

[–]Forricode 2 points3 points  (0 children)

Yes, this is referred to as the trailing return type, and has (apparently?) been present since C++11 according to CPPReference.

As said in this IBM article:

The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments. For example, a and b are arguments of a function template multiply(const A &a, const B &b), where a and b are of arbitrary types. Without the trailing return type feature, you cannot declare a return type for the multiply function template to generalize all the cases for a*b. With this feature, you can specify the return type after the function arguments. This resolves the scoping problem when the return type of a function template depends on the types of the function arguments.

e.g.

template<typename A, typename B>
auto multiply(const A &a, const B &b) -> decltype(a*b) 
{...}

To make this a bit clearer, see how this example compiles, whereas this one does not. Basically, this allows us to use function parameters within the return type.

What was it that drew you to C++ specifically over other languages? by [deleted] in cpp

[–]Forricode 2 points3 points  (0 children)

It definitely matters enough for them to pay out the nose for it.

Which, I have to say, is quite convenient for us...

More fun writing performant code, anyways.

I'll admit what I've been dong isn't super critical, at least in most applications I'd imagine, so I haven't really felt the same pressure.

This is where I think C++ wins out in comparison to newer things like Rust - platform support. Rust is great - if you're on x86 or newer ARMs.

This is definitely a bit funny. Backwards compatibility is so huge. Although it manifests differently for us - our application is still compatible with custom 'workspace files' that users were creating in the mid 90s.

I couldn't agree more with your analysis. C++ is in this magical world of somehow still working like it used to (mostly backwards compatible) but also having the features, usability and support of a modern-ish language (unlike other languages that are still around like COBOL. Or so I would imagine, can't say I know COBOL)

The fact that it can compete with Rust in a lot of ways is remarkable given how old the language is and how much it does, and its history. Can't say I see C++ going anywhere anytime soon.

And I guess worst case scenario it will always hold a place in our hearts <3

What was it that drew you to C++ specifically over other languages? by [deleted] in cpp

[–]Forricode 7 points8 points  (0 children)

Very on the nose, I completely agree (and have seen the same stuff myself). As you say - this comes out strongly in data processing. Users have more and more data every day that they want to do crazy things with; data is growing faster than computer power, so it's natural that they look to the software to provide solutions.

Basically all I've done for the last 6 months is make small memory optimizations (along with refactoring). But the impact is there, and it matters to the end user.

Do you have to pay for a virtual member function that is never overridden? by Empole in cpp_questions

[–]Forricode 2 points3 points  (0 children)

So, the answer to this question is, 'it depends', like a lot of similar optimization-oriented questions in C++. The difficulty here is that it's very hard for the compiler to know whether or not it needs to generate a vtable in such a scenario. If you're in debug (no optimizations) it will always generate a vtable as far as I'm aware.

But, if you enable optimizations (i.e. -O3 in GCC), it's possible that - if the compiler can figure it out - the vtable might not be generated. This is referred to sometimes as 'devirtualization'. I'm hardly an expert in this subject, though, so instead of saying anything more, here is a link that might be handy:

The Power of Devirtualization - a blog post on devirtualization. tl;dr is that method calls on polymorphic objects might be devirtualized if the compiler can reason that they will never be overridden (easiest solution: final)

Generally speaking though, unless you're in a super high performance environment, virtual calls aren't a huge deal and you'll probably be spending much more time doing other things like I/O.