C++ Weekly - Ep 125 - The Optimal Way To Return From A Function

Поділитися
Вставка
  • Опубліковано 22 лип 2018
  • ☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
    Upcoming Workshop: Understanding Object Lifetime, C++ On Sea, July 2, 2024
    ► cpponsea.uk/2024/sessions/und...
    Upcoming Workshop: C++ Best Practices, NDC TechTown, Sept 9-10, 2024
    ► ndctechtown.com/workshops/c-b...
    Upcoming Workshop: Applied constexpr: The Power of Compile-Time Resources, C++ Under The Sea, October 10, 2024
    ► cppunderthesea.nl/workshops/
    T-SHIRTS AVAILABLE!
    ► The best C++ T-Shirts anywhere! my-store-d16a2f.creator-sprin...
    WANT MORE JASON?
    ► My Training Classes: emptycrate.com/training.html
    ► Follow me on twitter: / lefticus
    SUPPORT THE CHANNEL
    ► Patreon: / lefticus
    ► Github Sponsors: github.com/sponsors/lefticus
    ► Paypal Donation: www.paypal.com/donate/?hosted...
    GET INVOLVED
    ► Video Idea List: github.com/lefticus/cpp_weekl...
    JASON'S BOOKS
    ► C++23 Best Practices
    Leanpub Ebook: leanpub.com/cpp23_best_practi...
    ► C++ Best Practices
    Amazon Paperback: amzn.to/3wpAU3Z
    Leanpub Ebook: leanpub.com/cppbestpractices
    JASON'S PUZZLE BOOKS
    ► Object Lifetime Puzzlers Book 1
    Amazon Paperback: amzn.to/3g6Ervj
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 2
    Amazon Paperback: amzn.to/3whdUDU
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Object Lifetime Puzzlers Book 3
    Leanpub Ebook: leanpub.com/objectlifetimepuz...
    ► Copy and Reference Puzzlers Book 1
    Amazon Paperback: amzn.to/3g7ZVb9
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 2
    Amazon Paperback: amzn.to/3X1LOIx
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► Copy and Reference Puzzlers Book 3
    Leanpub Ebook: leanpub.com/copyandreferencep...
    ► OpCode Puzzlers Book 1
    Amazon Paperback: amzn.to/3KCNJg6
    Leanpub Ebook: leanpub.com/opcodepuzzlers_book1
    RECOMMENDED BOOKS
    ► Bjarne Stroustrup's A Tour of C++ (now with C++20/23!): amzn.to/3X4Wypr
    AWESOME PROJECTS
    ► The C++ Starter Project - Gets you started with Best Practices Quickly - github.com/cpp-best-practices...
    ► C++ Best Practices Forkable Coding Standards - github.com/cpp-best-practices...
    O'Reilly VIDEOS
    ► Inheritance and Polymorphism in C++ - www.oreilly.com/library/view/...
    ► Learning C++ Best Practices - www.oreilly.com/library/view/...
    ChaiScript: chaiscript.com
    Music: www.bensound.com/royalty-free...
  • Наука та технологія

КОМЕНТАРІ • 172

  • @mohammadghasemi2402
    @mohammadghasemi2402 6 місяців тому +2

    We know that the compiler can optimize many things. But we also know that the compiler does not know our intents at the highest levels of abstraction. This episode made me realize that we should know exactly what is the highest level the compiler can understand our intent. Because it can not optimize further than that. Also now I understand the value of what Mr. Stroustrup always says: The code should reflect our intent. On reason for that is that the compiler also understand our intent and optimize things away.

  • @surrog
    @surrog 6 років тому +62

    iirc, the whole "one return" policy started with the necessity to guarantee that, in C, calls to release handles to system objects are ran every time. So, yeah, in full modern Raii codebase, it no longer make sense, and you can freely avoid creation of an empty object.

    • @PeterSmithson
      @PeterSmithson 5 років тому +3

      Yep. Took me a while to switch over after spending so long with C coding.

    • @lumpyfishgravy
      @lumpyfishgravy 3 роки тому +1

      It especially makes no sense if you use throw.

    • @adul00
      @adul00 3 роки тому +2

      Yup.
      It is also a MISRA C rule, and a rule I hated absolutely the most.
      Not only it made the code much harder to write and read, but it also completely wrecked the cyclomatic complexity (that was also being checked...).
      And the best part? Drivers we wrote didn't allocate / release any resources at runtime.

    • @JohnDlugosz
      @JohnDlugosz 2 роки тому +1

      That's a practical benefit, but I think the idea also comes from a misunderstanding of something in the original "structured programming" paradigm shift some time back. It refers to "single return" but it has nothing to do with how many return statements are inside a function; rather, it has to do with where the code resumes after the function returns: it's such an alien concept today, but you could have different success/failure return points or more generally fold conditional branching on the return value into the function code itself. The closest thing today would be a function that throws a different exception to classify the result, rather than returning a value representing that classification, and you invoke it using a try block with different catch's for each case.
      Finally, another practical benefit for having a single return statement is ensure that RVO is triggered, by turning it into NRVO instead which always works.

  • @Sluggernaut
    @Sluggernaut Місяць тому +2

    This was absolutely astounding. Thank you! I've been trying to get back into reading the asm for my instructions and this was very helpful!

  • @EllipticGeometry
    @EllipticGeometry 6 років тому +62

    Somehow the main thing I noticed is that the "90" part of "1234567890" is 12345 in little endian. Funny constants are distracting.

  • @jerem_
    @jerem_ 6 років тому +27

    I'd even go a bit further (and take advantage of string literals) if I were to do it with a ternary: return b ? "Hello"s : "Worlda"s;. ^^

  • @oisnowy5368
    @oisnowy5368 4 роки тому +4

    Just imagine how much better the world would have been had C come with a native string datatype.

    • @superscatboy
      @superscatboy 3 роки тому

      I suppose that at the time a pointer to a char seemed like a neat and simple way to manipulate text that didn't require much overhead, so a native string type seemed redundant. I'm only guessing though.

    • @nickthurn6449
      @nickthurn6449 2 роки тому +1

      ... and in the finance world a native date (not a datetime a date)

  • @Snijele
    @Snijele 4 роки тому

    Hey, thanks for this insight. This was realy helpful.

  • @ruadeil_zabelin
    @ruadeil_zabelin 6 років тому

    Interresting stuff! Thanks

  • @kumarcgowda
    @kumarcgowda 4 роки тому +2

    This actually made me go back to programming school. Really appreciate your time and efforts.

  • @treyquattro
    @treyquattro 3 роки тому +6

    ASCII string literal "90" is 12345 in decimal. Who knew!?!

  • @perceval.a
    @perceval.a 5 років тому +8

    Very interesting ! But why letting this « else » statement in multiple return path ?
    Readability and maintainability ? I do prefer multiple return path because it allows to remove lots of extra lines/indentations level. In the end, the less lines you have to deal with, the easier it is to review the code.

  • @streamdx
    @streamdx 4 роки тому +2

    So just don't ask the compiler to default-construct your retval;
    std::string get_value(bool b)
    { //consider b==false most of the time
    std::string retval{"Hello"};
    if(b) //sometimes its true
    retval = std::string{"Worlds"};
    return retval;
    }

  • @danielphd5072
    @danielphd5072 6 років тому

    It is impressive to see the code generated by the compiler when you add std::string. The code is much less long.

  • @JohnDlugosz
    @JohnDlugosz 2 роки тому +1

    Some of us are stuck with targeting code for RH/CenOS 7 which does not have SSO but uses the "old" string implementation in the standard library. That prevents the single-64-bit constant code from working, as it always has to allocate and copy.
    I'm currently leaning to using sv suffix instead of lexical string literals, to preserve the length knowledge _in general_ when the compiler deals with const char* as strings.

  • @atimholt
    @atimholt 5 років тому +1

    This video was suggested to me exactly one year after it came out. Sounds like that must be part of the UA-cam algorithm, or something.

  • @sera_kath
    @sera_kath 2 роки тому +7

    While working on the bytecode-VM for my little programming language I noticed a big performance boost using mutiple return paths. Even in void functions.
    if and return was much faster than a chain of if else. About 20-30% speed gain.
    I got those results on GCC-10 with -O3 and c++20. The compiler was able optimize a lot more in this case.

  • @Sebanisu
    @Sebanisu 3 роки тому

    I tend to mix a little bit. If I don't have all the info I need till later I might create a ret variable to have something to write data too as I'm reading it. But when I have a branching path where it's like. Is the data compressed or not? Then i'll use multiple return paths. I'm not sure how correct this all is but that's how things are. Thank you for all these videos.

  • @jimvonmoon
    @jimvonmoon 6 років тому +8

    Don't exceptions already make keeping single return path pointless?

  • @DamianReloaded
    @DamianReloaded 6 років тому +6

    Great episode. It is easy to not think about what the compiler will do at all. Tho no every programmer will have an encyclopedic (and updated) knowledge of every compiler to know these things from hearth when in doubt you can at least check the generated assembly. ^_^ if (b) return "foo"; return "bar"; //ftw

  • @thevinn
    @thevinn 6 років тому +1

    Looks like a great plug for Compiler Explorer :) :) :)

    • @JohnDlugosz
      @JohnDlugosz 2 роки тому

      Yes, I'm surprised he doesn't have to flag it as "product placement" in Europe. :)

  • @ric.larsson
    @ric.larsson 3 роки тому +4

    This seems like the wrong conclusion. It's not that you should use independent return statements but that you should not use C-types as strings. If you use strings, why are you using single-value return statements to the implicit constructor? Isn't the conclusion instead that you should use single-value return statements if you want to rely on implicit constructors?
    I like the episode, it makes a lot of sense to keep in mind that compilers are not always able to do their magic for allocating types.

    • @JohnDlugosz
      @JohnDlugosz 2 роки тому +1

      My thoughts exactly. It's not about `return` at all. You get the same issue when writing:
      const std::string prefix = someflag ? "value one" : "the other one";
      and because of the desire to use const we are led to using the ternary operator. But this will generate bad code for the same reason.
      The issues in the code are actually:
      - using a computed value as the argument of a constructor, _especially_ in the case of lexical string literals where the optimizer can constexpr the strlen when the value is known at compile time;
      - assigning to a default-constructed instance rather than initializing it when you are ready for it.
      - ensuring NRVO vs _maybe_ getting NVO if every return statement is compatible with that.
      I think the real benefit to presentations like this is that it encourages people to try their own ideas and play around in Compiler Explorer.

  • @jgreitemann
    @jgreitemann 6 років тому +12

    I thought multiple return paths were the correct answer, but for a different reason. Doing multiple returns allows the compiler to verify that the condition is exhaustive. Now, as long as there's an "else", that's not much of an issue, but you might be tempted to have some more complicated logic in place which you think is exhaustive but it isn't. In that case the compiler would force you to reconsider and at the very least throw or assert or whatnot. In the single return path version, you might just return the default constructed (or uninitialized for PODs) value.

    • @lumpyfishgravy
      @lumpyfishgravy 3 роки тому +1

      Some style guides eg MISRA don't like multiple returns. I don't particularly like MISRA.

    • @axelBr1
      @axelBr1 2 роки тому +1

      I recently returned to programming in C++ after many years of not programming, and as I was developing my application I had to ponder many returns verses a single return when I had a situation that required the calculation based on many parameters. I didn't consider optimum code generation, rather for me it was more important that the code be easy to read. With a single return path, either every evaluation criteria has to be nested, (which leads to horrible levels of indentation (impossible to read and work out what is or isn't true)) or, every evaluation has to "exclude" all the conditions that have been checked but aren't true, also horrible to workout what is or isn't true.
      By using multiple return paths, one condition at a time is evaluated, and if true, we know we're going to leave and never have to worry about it again in the rest of the function.
      As you say, if you get to the final return, either something is wrong with the conditions to evaluate against and we can either return the default constructed object or uninitialised POD or through an exception or assert(false) or we are okay and return a harmless value.

  • @shavais33
    @shavais33 4 роки тому +5

    I agree with this completely, and I'm gratified to come across a like-minded soul. The "single entry, single exit" rule is an ancient, outmoded practice that hails from the Fortran days. If early returns are a problem for you, then I would submit (as Jason more-or-less does) that your coding style has other, worse problems. I would further contend that if minimizing time to market and programmer pain is a higher priority for you than maximizing performance, then you shouldn't be working in C++ at all. All that said, it's not my impression that this post was intended to make any recommendation concerning how to approach writing production code, but rather merely to point out which of these idioms is more optimal as far as performance goes.
    Very interesting and informative post.

  • @pointyhairedboss1
    @pointyhairedboss1 3 роки тому

    why did the compiler not do NRVO on the version with a single return statement ? I thought C++17 guaranteed copy elision ?

  • @foomoo1088
    @foomoo1088 Рік тому +1

    It would be interesting to explore this concept with other return types….not sure the general recommendation “prefer multiple return paths” is optimal in other cases (including switch statements). For return types that fit into registers I would guess perf would be comparable for either multiple/single path. Also this simple example is a bit contrived since real world scenarios typically have a chunk of “other stuff” going on in the additional paths.

    • @cppweekly
      @cppweekly  Рік тому +2

      For trivial objects of any type or size this example becomes irrelevant. The reason this example is not contrived is because we rarely think about trying to make as much of our API trivial as possible. Also, yes, you can *almost always* refactor your code to look like this by composing multiple functions, which can result in much more optimizable code.
      See also: ua-cam.com/video/ZxWjii99yao/v-deo.html

  • @FreeDomSy-nk9ue
    @FreeDomSy-nk9ue 2 роки тому

    I don't understand! Why would the compiler ever generate a call to strlen for a string literally when the length is available at compile time?

  • @younesmdarhrialaoui643
    @younesmdarhrialaoui643 7 місяців тому

    Why not making the return type of the get_string() function a string_view? You only get the string anyway.

  • @MsHojat
    @MsHojat 6 років тому

    Wow this is cool. It's mostly gone over my head since I do very little c family programing or even programing in general, but it's still neat.

  • @AndrewHelgeCox
    @AndrewHelgeCox 3 роки тому +1

    Need to watch out for impacts on branch prediction going from one ret to two.

    • @AndrewHelgeCox
      @AndrewHelgeCox 3 роки тому

      en.wikipedia.org/wiki/Branch_predictor#Prediction_of_function_returns

  • @Ikogn
    @Ikogn 6 років тому +12

    What do you think of the "my string"s (string literals) method for creating an std::string?
    I would have used something like:
    std::string getValue(const bool b)
    {
    using namespace std::literals;
    return b ? "Hello"s : "World"s;
    }

    • @asuasuasu
      @asuasuasu 6 років тому +11

      Should be the same as `return b ? std::string("Hello") : std::string("World")`.

  • @uzimonkey
    @uzimonkey 6 років тому +1

    How did we get by without compiler explorer?

    • @srccde
      @srccde 4 роки тому +1

      objdump

  • @SG-kn2jl
    @SG-kn2jl 4 роки тому

    Why don't u use *using namespace std*?

  • @yash1152
    @yash1152 Рік тому

    6:18 > _"disassembly"_
    u meant assembly right? or disassembly of the c++ code? or both ;)

  • @Erarnitox
    @Erarnitox Місяць тому

    at 8:00 wouldn't return std::string(b?"Hello":"World"); also work? and it would be easier to read/write I think.

    • @cppweekly
      @cppweekly  Місяць тому +1

      Not as well as you'd expect, because you have to consider the result type of the ternary expression. It's a `const char *` as opposed to a `const char []` (which is what "Hello" is) so the compiler is not able to optimize in the same way as it can with two completely different return statements.

  • @antonw8134
    @antonw8134 6 років тому +7

    Count me as someone that prefers an implicit else. ;^} I’m generally more concerned about understanding the context of the multiple conditions and structuring the returns such that the most probable condition returns first. It’s true that sometimes you won’t know... At which point it’s necessary to break out the tape measure (if you can ever find it).

    • @superscatboy
      @superscatboy 4 роки тому +1

      Same. I find it weird that the folks at clang think this is a problem for readability - I find it to be the opposite.
      clang.llvm.org/extra/clang-tidy/checks/readability-else-after-return.html

    • @SK83RJOSH
      @SK83RJOSH 3 роки тому +1

      @@superscatboy he said implicit, as in, implied but not explicitly there. So I think he means he prefers clangs style, and I do as well for the record. It reduces visual noise, particularly in the case of return values and so on. 😉

  • @yash1152
    @yash1152 Рік тому

    > 12:51 _"one [line of code to a function]"_
    won't that increase the function calls and calling records on stack? I genuinely wanna know ur thoughts!
    The book chtp-6e (quite old) or c++htp-8e by deitel-deitel always emphasize on reducing function calls for small things.

    • @cppweekly
      @cppweekly  Рік тому

      Not with return value optimization. Also - compilers are very very good at inlining and eliminating small function calls.

  • @pooya130
    @pooya130 5 років тому +4

    Jason would you be able to specify the compiler version since the videos would be dated after a while?

    • @ThomasHume-Aszarsha
      @ThomasHume-Aszarsha 4 роки тому +2

      You can see the version at the bottom of the windows. GCC 9.0.0 and Clang 7.0.0 here.

  • @larswadefalk6423
    @larswadefalk6423 2 роки тому +1

    I couldn't really follow the difference between using the ternary operator and the if else clause, other than some internal compiler logics. From my point I "see" an implicit else clause in the ternary operator, it should in my mind be the same.
    I see comments below mentioning the difference that the ternary operator is more of an atomic "macro" like operation rather than an "evaluation" option, in that case I would consider the ternary operator to be questioned in all possible optimisation cases.

    • @elijahshadbolt7334
      @elijahshadbolt7334 2 роки тому

      The ternary operator with string literals is treated as an expression with temporary type `const char*`, which does not include length. That const char* is then used to construct a std::string, so in the general case where the two result operands could be a const char* created at runtime from a function call, it would not be optimised. I don't know why it isn't optimised in this video tho

  • @DoDoENT
    @DoDoENT 6 років тому

    Wow!

  • @snow8311
    @snow8311 6 років тому +23

    I somehow fail to see how the ternary operator differs from a simple if-else, good video though!

    • @hex7329
      @hex7329 3 роки тому +9

      A ternary isn't a branch in execution, but rather a value. An if/else leads to 2 different paths where different instructions are executed. A ternary represents one of the 2 values on its right side.
      So an if/else is different stuff to do.
      The ternary IS the string or integer or whatever you put in there.

    • @codyheiner3636
      @codyheiner3636 3 роки тому +3

      Ternary evaluates to a const char *, which then must be used to initialize a string, but since the const char * came from a ternary it's length is not known hence there's extra work to do.

    • @JohnDlugosz
      @JohnDlugosz 2 роки тому +1

      @@hex7329 The ternary makes the const char* a calculated thing, so the compiler no longer knows the length without calling strlen.
      The if/else constructs a different string on each branch.
      It's not that if/else is different per-se, but you are also doing different things in the branches in each case. The ternary would be equivilent if you put a std::string in each branch of it rather than factoring it out and constructing the string around the result.

  • @salimp2009
    @salimp2009 4 роки тому

    awesome video..& videos :) I have a question ; is it safe to return a string_view from a function looking at the example in the video. Does not the value that string_view will be destroyed ? Just asking to learn...

    • @tatoute1
      @tatoute1 2 роки тому

      Do not return string_view. String view borrow the string content somwhere and offer no garanties about this somewhere lifetime. It's open bar for dandling pointer.

    • @oracleoftroy
      @oracleoftroy 5 місяців тому +1

      It is safe for string literals like in the example.
      std::string_view f() {
      // Safe
      return "hi";
      // p points to a string literal
      auto *p = "fine";
      return p;
      // Unsafe, returns pointer to local storage
      return std::string{"bye"};
      std::string s{"bad"};
      return s;
      }

  • @vorpal22
    @vorpal22 5 років тому +1

    (0:22 in)
    I prefer multiple return paths only IF the conditions are simple and it allows me to avoid nesting a whack of if statements or more than a depth 1 loop break (or using status variables to force multiple loop breaks).
    if (A) { ...} else { if (B) {...} else {...} } is much less nice than if (A) {... return} if (B) {....return} C.
    I love your videos a lot (and have only recently got back into C++ after a 10 year hiatus, so I'm trying to catch up on C++ 11 / 14 / 117) and you have taught me a lot of fascinating things, but in some cases, I think that clear, easily read code with less nested blocks, while possibly being less efficient, is preferable to the hyper-efficient but less readable code. Many C++ programmers are obsessed with optimized code, which often isn't necessary, and this is coming from someone who has a PhD in combinatorial design theory where even small optimizations can make the difference between tun times in hours vs. days.

  • @AndrewHelgeCox
    @AndrewHelgeCox 3 роки тому

    Best way to return a string-like object perhaps, but lots of functions return primitive types and POD. Title should be be narrowed in scope.

  • @sephirostoy
    @sephirostoy 6 років тому +7

    I was wondering why you didn't show with MSVC too. I did try and ... it was horrible: about 20x times more assembly lines generated for no reason :o

    • @yan-amar
      @yan-amar 5 років тому +6

      I think thise series consider only serious production compilers :p

    • @mstorer3772
      @mstorer3772 5 років тому +3

      I think you just answered your own question... also keep in mind that fiddling with optimization flags can give radically different output.

  • @QIZI94
    @QIZI94 3 роки тому +2

    heaving return type as const std::string_view would be better replacement for cont char*, since you only need return "literal"; without std::string("literal")
    Ofcourse if you want to mutate the returned string and you do not really care about heap allocation, then return should be std::string.

    • @tatoute1
      @tatoute1 2 роки тому

      Do not return either string_view neither char*. It's bare open code for dandling pointer. string_view is only useful as an argument, if any.

    • @QIZI94
      @QIZI94 2 роки тому

      @@tatoute1 right, but I am only saying that if you necessarily have to return const char*, rather use std::string_view since it can be used safely e.g. over range loops

    • @qqshutup7175
      @qqshutup7175 8 місяців тому

      @@tatoute1 not exactly, strings stored in the binary don't need to be copied and moved to the heap, so returning std::string_view is a better decision

  • @SeppyYT
    @SeppyYT 5 років тому

    Hallo, what is your opinion about using continue and break in loops?
    I tried Compiler Explorer with gcc 8.2 and -O3 flag and following code:
    #include
    void calc(){
    for(unsigned int i = 0; i < 1000; i++){
    if(i%2!=0) continue;
    std::cout

    • @kwinzman
      @kwinzman 5 років тому

      benchmark it. also try if using likely/unlikely on the if expression changes the ASM emitted.

  • @maxx666mayhem
    @maxx666mayhem 3 роки тому

    Very interesting subject here... We return from functions so casually but never knew what was the performance impact of each approach

  • @subhransumahapatra6023
    @subhransumahapatra6023 4 роки тому

    This good content. Precisely the reason to love C /C++. So much scope for compile optimization.

    • @superscatboy
      @superscatboy 3 роки тому +2

      This is about C++, not C.
      If you try this technique too much in C you'll eventually get into trouble because C doesn't have RAII. Make no mistake - C and C++ are *very* different languages.

  • @stephenhowe4107
    @stephenhowe4107 4 роки тому +3

    One more possibility:
    return std::string(b ? "Hello" : "World");

    • @mikezhu5894
      @mikezhu5894 4 роки тому +1

      I checked it will still call strlen. using gcc compiler

    • @stephenhowe4107
      @stephenhowe4107 4 роки тому

      @@mikezhu5894 : Yes maybe so. But that is a quality of implementation of the compiler. There is a std::string constructor which takes a char *, a size and an allocator. The compiler knows the string length of either option. So it could easily optimise a call to a string constructor of 3 parameters, losing the 1st parameter in some form of conditional move. And after this do further optimisation of the constructor so it is still a string but it is constructed inline.
      So yes, compilers can go further with this construction as Jason noted in his clip.

  • @LesleyLai
    @LesleyLai 6 років тому +2

    I have some strong opinion around this topic since I have an instructor in my university forbid us using multiple returns. The reason is basically "this is a best practice that my instructor forced me to do." If her argument was something more reasonable like resource management concern, then I will at least try to debate.

    • @philipbotha6718
      @philipbotha6718 5 років тому +1

      Many industry coding standards, MISRA, AUTOSAR etc mandate a single entry single exit to functions.

    • @axelBr1
      @axelBr1 2 роки тому +1

      About 35 years ago I was in my first year of a Chemical Engineering degree and we had 1 lecture a week on programming. Because the University has a computing science faculty, we were taught the CompSci approved language for programming, which was PASCAL. At the end of the academic year we had a short cause on FORTRAN because that was the computing language that industry actually uses. And a few years later, I did begin using FORTRAN for my job, and have never heard of anyone in industry using PASCAL, (although IEC 61131-3 Structured Text for programming programmable logic controllers is quite close to it).
      Tip, if you every want to upset an instructor, just prefix a comment on their course with "In the real world".

  • @bobkerner7845
    @bobkerner7845 5 років тому +1

    When using multiple returns within a function, it might be useful to discuss the impact of RVO when highlighting "optimized" techniques.

  • @GuillaumePicquet
    @GuillaumePicquet 6 років тому +3

    I tried constexpr but compiler warn about converting string litterals to char *

    • @asuasuasu
      @asuasuasu 6 років тому +4

      I assume your declaration was something like `constexpr char* blahblah();`? If so the return type is `char*`, not `const char*`.
      You're marking the function as `constexpr`, not the return value. In that context, it wouldn't make sense either. And indeed, conversion from a string literal `"hello"` to a non-`const` `char*` is illegal in C++, hence the warning.

  • @KennyMinigun
    @KennyMinigun 6 років тому +3

    There is also an opinion, that having multiple return statements can cause RAII bloat, with each statement generating a different destruction code (because of the scope it is being in).

    • @dagoberttrump9290
      @dagoberttrump9290 Рік тому

      That is interesting. If so does the destructor change the instruction binary according to the context it is being called with (e.g. data members change inside scope)? That would then be a compile time optimization and lead to mlre performant code (with the caveat that it bloats the binary)

  • @danielrhouck
    @danielrhouck 6 років тому +2

    Of *course* it shouldn't be `const char*`. You may or may not need to put it into a `string` right away, but if you don't want to do that you should at least use a `string_view`.

  • @SamJohnsonking
    @SamJohnsonking 6 років тому

    Any plans to add Deep learning to the compiler?

  • @skyeplus
    @skyeplus 3 роки тому +2

    I'm a bit confused and surprised what RVO is working in this case. It's two returns with condition on argument.

  • @locomotivere4497
    @locomotivere4497 4 роки тому

    drag and drop

  • @richardcavell
    @richardcavell 5 років тому +1

    None of this is the compiler’s fault. You need to put the “static“ keyword in front of your function prototypes to get the optimizations you want.

  • @LunarLambda
    @LunarLambda 6 років тому +5

    return (void)"Everything is fine.";

  • @lumpyfishgravy
    @lumpyfishgravy 3 роки тому +4

    The always correct answer is: "follow your company's style guide." But if you have a choice, do what makes for the most readable code.
    If your compiler makes a sub-optimal job of it and you actually care, change your compiler.

    • @tatoute1
      @tatoute1 2 роки тому +2

      "follow your company's style guide", no, fight stupidity in style guide. If the style guide choose between CameCase or use_underscore, fine. If the style guide is about the number of return or the length of the function, contest it. Mandatory things like this is bullshit stupidity, created by morons, just to find something to says in code reviews.
      Here is the ultimate code style:
      - make your code understandable
      - use whatever tools the compiler offer you to reduce the probability of bugs (ex make everything const except if not possible).
      - do not use new forms/things/syntax just because it exists. If you want to experiment, do it outside of production code.

  • @theRECONN
    @theRECONN 6 років тому

    Could you do a video about inline and differences between inline in method declaration vs inline in method definition? I've always thought inline in definition inlines methods body to the class' body while inline in declaration suggests the compiler to replace function calls with function's body. Also I could never come up with an example to show the advantages of `inline` in Compiler Explorer

    • @nonsensation
      @nonsensation 6 років тому +1

      inline has nothing at all to do with inlining a function

    • @theRECONN
      @theRECONN 6 років тому

      wow, now I am even more confused

    • @krzysztofwaleska
      @krzysztofwaleska 6 років тому +2

      General video concerning inlining and compiler optimisations would be nice. I got an impression that lately inline doesn't do much. Just little tip for a compiler. In my first compilers it meaned that it will inline that function.

    • @erichopper4979
      @erichopper4979 6 років тому

      reconn it used to have a *lot* to do with whether or not a compiler would inline something. A little like the now deprecated (or maybe actually removed?) register keyword that hinted to the compiler that it ought to keep a variable in a register.

    • @theRECONN
      @theRECONN 6 років тому

      Right, but register is as frequent as a goto nowadays yet I still see inline in modern code strangely enough. Wonder if people know what it does.

  • @marktaylor6052
    @marktaylor6052 4 роки тому

    Try returning a const char* instead of a std::string which is way overengineered for the problem. Just because you can use STL instead of C does not mean you always should.

    • @TheMrKeksLp
      @TheMrKeksLp 3 роки тому

      No you absolutely always should. In fact you shouldn't even use C++, use Rust. But if you have to use C++ for the love of god avoid raw pointers like the plague

    • @marktaylor6052
      @marktaylor6052 3 роки тому

      @@TheMrKeksLp Rust is dead. C++ 20 is King, and raw pointers are fine for me. I don't have problems with them, and I certainly don't need to wrap up every piddling constant string in a class.

    • @ronaldweasly561
      @ronaldweasly561 2 роки тому

      @@TheMrKeksLp borrow checking is not Free

  • @huyvole9724
    @huyvole9724 6 років тому

    What name os/distro you using ?

  • @jkrigelman
    @jkrigelman 2 роки тому

    That else has no reason to exist in the multi return except to have extra indentation. This example it does not really matter though. I like the multi returns. I remember professors pushing for the one return method.

    • @cppweekly
      @cppweekly  2 роки тому

      the else doesn't matter ... except when it does.
      Example: if constexpr, if consteval, and older gcc compilers in the case of a constexpr function that just happens to have an if statement in it.
      I always use the else, it makes me happy and its consistent with my code.

  • @anupkodlekere3633
    @anupkodlekere3633 4 роки тому

    Can someone explain why the ternary operator returns a const char* and then it changes it to the string?

    • @AntonioBarba_TheKaneB
      @AntonioBarba_TheKaneB 4 роки тому +2

      because the ternary expression is, as the name implies, an expression. So you are converting the const char* to std::string AFTER the expression evaluation, but if you use the if-else code you have no expression to evaluate, you are just converting one literal value directly to std::string

  • @jansimblank9358
    @jansimblank9358 6 років тому

    This version seems to generate considerably less assembler code on VC++
    return c_STRS[b_?0:1];
    Can you comment about this?

  • @snickl1
    @snickl1 6 років тому +8

    Interesting insights, but I also think you are putting too much emphasis on optimization. In general, one should prefer code that expresses the intent most clearly and succinctly, which in this simple case is by the ternary. Compilers are getting smarter all the time, and unless it is really necessary, one should not write code that is harder to read to accommodate a compiler that will be dumb by tomorrow's standards.
    As for the return style, I think the disconnect is that people that argue for single-return are thinking about functions that are much too long and complex to begin with. I prefer multiple returns, structured to first get any preconditions out of the way one by one, with the actual work at the bottom of the function. But these functions are hardly ever longer than half a screen (at 80 columns), so I'm never confused as to where all my returns sit (also helped by syntax highlighting and a blank line after each return).

    • @EllipticGeometry
      @EllipticGeometry 6 років тому +1

      People have been saying for decades that smarter compilers will take care of it. While they are getting smarter, they're just never quite there. Looking at generated code is still often a nightmare. It's okay if you aren't doing that much work. But if you want or need your code to be lean, it can be helpful to be aware of patterns a compiler can or can't turn into something good. I'd recommend not overfitting your style to a particular compiler, but choosing constructs that translate to good machine code with few transformations. That should work on a range of compilers and not be too hard on you.
      I agree multiple returns can be clean and readable. Speaking more generally, after carefully splitting code paths based on preconditions, don't make them join again unless there's commonality on what needs to happen next. Obsessive joining only makes it hard to tell when a particular condition is handled and done. Paths applying to many conditions are more complex and bug-prone. Anything could potentially still happen, either by design or by bug. You won't know without analyzing the entire function.

    • @snickl1
      @snickl1 6 років тому +1

      I guess there is a performance lesson to take away from this video, but it's not about return style at its core. The real key seems to be that object construction from literals should be done explicitly and straight from the literal, because a compiler is most likely to take shortcuts with a source literal that is immediately visible as a constructor argument. If the value used for initialization is obscured even by just very simple logic (such as a ternary) before going into the constructor, these optimizations might already fail to kick in.
      Returning literals from a function by multiple return is how you get literals into the implied return type constructor directly, but that is just one (albeit clever) application of a larger principle.
      He kind of took the wrong path, but still got to the right conclusion :-)

    • @taragnor
      @taragnor 4 роки тому +3

      Honestly if you're programming in C++, you should probably be worried about performance, especially in your functions you expect to get called a lot.

  • @streamdx
    @streamdx 4 роки тому

    MISRA and AUTOSAR requires to have only one return statement per function placed at the end of the function.

    • @streamdx
      @streamdx 3 роки тому

      Vendicar Kahn may be you don’t. But they are doing their job to ensure safety of your next ride...

  • @deadknight3041
    @deadknight3041 6 років тому

    It's no matter which way is better if you have to use strict coding rules. For instance the MISRA doesn't allow to use multiple return points in functions

  • @amr3162
    @amr3162 6 років тому +2

    -11:50-- i think all of people would prefer longer videos over 5-6 min video.-
    I'm wrong. however i would still like it if you could do a longer video once a in a while.

    • @jakearkinstall5313
      @jakearkinstall5313 6 років тому

      That's a shame. I certainly prefer longer videos, but if thats what the stats say then you have no real incentive for the extra effort. If you feel like experimenting, you could try longer ones for Patreon supporters, while using short ones for open youtube as patreon bait (A 5 on the scale from 0 to Bryan Lunduke).
      Also I'd like to see some more shout outs for CppCast. I've been following you for quite some time but only came across the podcast last week. It's a great listen!

    • @jakearkinstall5313
      @jakearkinstall5313 6 років тому

      I knew I had seen the name somewhere! Eh well, fool on me for not checking it out earlier. Though I keep a close eye on the std proposals forum, I'm somewhat removed from the progress further down the pipeline, and the podcast is a great way of keeping up with proposals, papers, notable opinions and viewpoints from developers who work in niche areas.

  • @MrAlbinopapa
    @MrAlbinopapa 5 років тому +1

    I thought multiple return paths prohibited RVO though?

  • @AndreasStenmark
    @AndreasStenmark 6 років тому +1

    Yeah, this is what makes C++ awesome and awful at the same time. I started a journey several years ago to really fully understand what happens with my code, and how to make it as fast as possible. I've seen lots of things like this along the way. Sometimes it makes code paths start running so fast that it seems to defy the laws of physics. Other times a tiny change can cause a seemingly unrelated slowdown, when you fill up the instruction cache for example. Ultimately, for all the control we have in C++, there's much more that we can't explicitly control from the language, and instead gets controlled by whoever made the CPU, whoever made the optimiser, whoever made the memory system, etc. There is indeed zero overhead abstraction deep inside of C++, but it's not always easy to lure it out from the shadows.

    • @longhaulblue1145
      @longhaulblue1145 4 роки тому +1

      Lol. I used to be enamored with C++ but not anymore. I learn this kind of stuff only when I cannot avoid it. When I write C++ I find myself thinking a lot about what the compiler might do instead of the problem I'm actually coding for.

    • @JohnDlugosz
      @JohnDlugosz 2 роки тому

      @@longhaulblue1145 So how is writing in another language better? If you just write non-optimal C++ won't it be at least as fast as the other language?

  • @PeterPetrakis
    @PeterPetrakis 6 років тому +13

    I'm disappointed that the compiler can't figure this out.
    I know it's easy to have a strong opinion on how compiler writers do their job. At the same time however it is difficult to defend or advocate the virtues of c++ because of stuff like this.
    We have to do better. The language is complex enough without having to concern one self with gaming the compiler.

    • @cicciobombo7496
      @cicciobombo7496 5 років тому +3

      Most people don't care, if you do care about top performance you would look at the assembly anyway.

  • @hwstar9416
    @hwstar9416 17 днів тому

    this isn't really a general rule tho? what I i'm not using std::string? heck what if not using any constructors?

    • @cppweekly
      @cppweekly  14 днів тому

      Understanding where and when return value optimization comes into play, when it is required, and structuring your code such that you take advantage of it, matters for anything that is bigger than about 2 registers.

    • @hwstar9416
      @hwstar9416 14 днів тому

      @@cppweekly it only matters if you're using constructors/destructors

  • @nelsonperrin5800
    @nelsonperrin5800 10 місяців тому +1

    Why not something like this?
    It shall be as fast as returning a const char *, if im not wrong.
    const std::string & get_value(const bool b)
    {
    static const std::array strings = {
    "1234567890",
    "qwerty"
    };
    return (strings[b]);
    }
    Edit: my remark is more about the return type and the static string than the array trick (it was faster to type than the if/else variant)

  • @DrGreenGiant
    @DrGreenGiant 4 роки тому

    I've recently started doing static constexpr char* to define all my return arrays. Then, my if else, or switch case, and return the char* by name. Is that equivalent to your suggestion? E.g.
    static constexpr const char* a="A";
    static constexpr const char* b="B";
    if (foo) return a;
    else return b;

  • @connorhorman
    @connorhorman 6 років тому

    There is a very definitive answer in this case. Adding the extra temporary value causes the return statement to use copy constructor of string vs move constructor of string. Copy constructor of string has the overhead of a dynamic memory allocation, whereas move does not. To make it fair, the single return path example should return std::move to bypass this overhead.

  • @wreckgar23
    @wreckgar23 6 років тому

    I thought multiple return paths inhibited copy elision. Clearly need to have a bit more of a play with this

  • @aMulliganStew
    @aMulliganStew 6 років тому +1

    After reading a few comments that take the position that isn’t mine, I’ve learned that I don’t work and play well with others when it comes to writing code. Oh, well.

  • @oleksandrkaleniuk7813
    @oleksandrkaleniuk7813 6 років тому

    The problem here is, if we think for the compiler too much, the compiler itself becomes a burden. We can do just fine doing LLVM directly, or writing in MSIL, or generating it in large chunks with any high-level language we choose. And it most cases this will be the most effective approach due to the level of control we'd have. Compilers are not essential for the computing, they're only accidental.
    There should be some kind of balance between the complexity the compiler reduces and the complexity it creates instead. Otherwise it's just impractical to have one.

  • @eboyarski
    @eboyarski 3 роки тому

    "If you wanted a string, you should have put a (st)ring on it" 🎶🎵

  • @PiotrWitoldNycz
    @PiotrWitoldNycz 6 років тому

    +1 - very good episode. But I still think const bool instead of just bool is unnecessary noice here.

  • @xealit
    @xealit 6 місяців тому

    This function is trivially short and does not reveal how to work with the compiler to make it optimise the return value. You are not returning literal strings in real code.

  • @MarcoBergamin
    @MarcoBergamin Рік тому

    I wonder why std::string does not have a constructor taking c-strings as input.
    My favorite solution in this case would be:
    #include
    template
    std::string to_str(const char (&str)[SIZE]) {
    return {&str[0], SIZE-1};
    }
    std::string get_value(const bool b) {
    return b ? to_str("Hello") : to_str("World!!!");
    }
    Considering that probably I would use 'to_str' many times.

    • @cppweekly
      @cppweekly  Рік тому +1

      Every compiler already has an optimization for constructing std::string objects from character literals.
      You would certainly get the exact same results if you simply did:
      return b ? std::string("Hello") : std::string("World")

  • @AndrewHelgeCox
    @AndrewHelgeCox 3 роки тому +33

    Jason, you have to stop calling code optimal after only eyeballing the disassembly. You with your experience must have read multiple books explaining to you the fallacy inherent in assuming anything about performance without taking some measurements.

    • @joestevenson5568
      @joestevenson5568 4 місяці тому +5

      Some assembly instructions are objectively and consistently more expensive than others. Testing is necessary for more complex systems, but not everything needs testing when it's literally one cpu instruction Vs another or just objectively doing less work.

  • @minciNashu
    @minciNashu 5 років тому

    MISRA forbids multiple returns.

  • @CanIHaveSomeMore
    @CanIHaveSomeMore 3 роки тому +1

    The example given in this video is too trivial. (Most) Real world functions would not return constant values, and could have more than 2 return statements. Also having a single return point makes it easy to debug what the function is returning - just add a single breakpoint on the return. With multiple return points you would need to find each one and add a break point for each.

  • @xavierthomas1980
    @xavierthomas1980 6 років тому

    I cannot agree with that! The only correct solution cannot use std::string because using std::string is never good. If you dig a bit deeper you probably would find an optimum way to return a string_view and actually never storing it in a string.

  • @LordXelous
    @LordXelous 6 років тому +1

    Good video Jason, however, I believe you're muddying the waters between Optimal/Efficient and Maintainable... You clearly demonstrate how optimal and efficient this code can be, however I would strongly argue it's not as maintainable as a single exit point solution. There are two return points in this function... That's twice as many potential tests and points of failure, more work, more things to maintain and more things for a new-comer to the code to learn about.
    Okay, this is a trivial example, but expand up across many more functions, each with more than one way out, with multiple conditions, states and expressions controlling the flow out and you suddenly exponentially increase the number of tests you need to write and maintain.
    Is this a bad thing?
    When targeting a product which demands the most optimal code, no. No, multiple end points are fine, they give a performance boost and achieve your product goal at the cost of more maintenance and a higher cost of entry to your code base.
    When targeting a product which demands low maintenance costs and ease of entry into the code base, then yes. Of course it's a problem, where you would have to look at applying structured programming technique to make the code-pathways as obvious as possible. On my personal coding standards list this means one exit point for every function.
    More than one exit point results in potential Spaghetti code.
    So when the whole purpose was to aim for maintainable code... Efficiency and Optimization at the compiler level is not really a consideration, in fact I've heard and read arguments against programmers writing code in an effort to second guess what an optimizing compiler will do (and they can argue that point much more eloquently than I).
    Overall, I see your points made, I like them, this is a good video, except at around that 12:58 mark, this is not the most maintainable code, you increase your exit points that automatically means more places to check for potential failure, adding more test requirements, more per-requisite tests to each point of exit. With one point of exit you can test range, test expectation and even know the flow the function must have taken all with one test, the power of this approach is to reduce points of failure, lower cost of comprehension and improve debug-ability... How many breakpoints to debug a function with one return point? Two at most, one at entry and one at exit... How many for a multiple returning function? 2 + N (where N is the number of return statements).

    • @frydac
      @frydac 6 років тому +4

      Because of multiple exits points there are not more tests, it's because there are if tests, which cause branching, which makes for more tests. The tests for both versions of a particular function should be exactly the same, as the function produces the same output for the same input. The value range is based on the return type and/or function contract/post condition, not the amount of return statements. As I've said in other responses, imho guard clauses clearly illustrate that multiple return statements can be more readable and maintainable. In case of the guard clause, the breakpoint thing also doesn't hold up: when you set a breakpoint at the end of a function, and the return value has some value you don't expect, you still need to step through the function, and potentially the whole function to be sure the return value doesn't change anymore. With guard clauses you'ld exist early out of the function and you don't need to look at the rest of the function.

    • @axelBr1
      @axelBr1 2 роки тому

      @@frydac Totally agree. (I agree with the original comment that readability trumps optimisation, at least on the first implementation), but agree with you on multiple exits making it easier to work out what's being tested. Guards at the beginning mean you can exit early and if you get to the end of the function you know something's gone wrong and handle it appropriately. And regardless of the number of exits you'll still have to step through the code to find which condition isn't correct, but with multiple exits, at least you only have to worry about one condition at a time.

  • @warlockd
    @warlockd 2 роки тому

    As an assembly language programmer I have to disagree. Calling conventions and endianness aside, you should always return the length of data with dangling pointers, and I don't give a damn if it screws up your opitemizations. Just assume calling function needs to know. This is the kind of shit that got Microsoft in a bind with 94-98 alll the way to now from you C/C++ programers. The whole REASON returns were one var was because it was shoved into the only regester so fine! Have 30 more them!
    At this point what is EVEN my job? I am spending more time in my mind numbed N2O haze of reincarnating 20 year hardware into some god damn Raspberry PI JUST so I don't have to FPGA the bitch to find out someone already did it 20 years ago, but the code was fucking blipped out because he called another coders pants "weird" (God I love old cvs comment commits) and they stopped making the IC chip that would make my life 500% easyer 2 years ago because of COVID. Its easier to build a state machine at this point than build another cpu but now my buzz is gone so my main point.
    Don't worry about your shit C++ code. Its doesn't matter how garbage it looks. Its not going to compare to the pleae of shit thinking that xoring the same register DOES anything to protect you from the boggy man of not documenting your shit. Or that the optimizer SHOULD know it NEEDS to be zeroed. Unless your working in that magic 60ms time loop for games, don't bother. Make it look pretty so people like us don't have to hunt you down and gut you like a trout.

  • @erichopper4979
    @erichopper4979 6 років тому +2

    I know some people who should see this. :-) Though, the people I'm thinking of would just blame C++ and say that this is just one more example of why it's a terrible language.

    • @ratgr
      @ratgr 6 років тому +3

      well they should show me the assembler of their language, lets get them clear in this you can just ignore all this, you'll just wont get optimal code-gen (not that you'll get it in another language ... well maybe rust).

    • @erichopper4979
      @erichopper4979 6 років тому

      ratgr the people I'm thinking of work with the assembly level output all the time and are very concerned with performance. They are well aware of C++s strengths. ☺

    • @ratgr
      @ratgr 6 років тому +1

      Then why is it a horrible language?, is there one that would do the same assembly with a better grammar/syntax? (i'm not sure i like rust syntax more)

    • @erichopper4979
      @erichopper4979 6 років тому

      ratgr - complexity, need to do your own memory management. I don't think many people have worked out that memory management systems destroy your ability to control memory layout and cache usage. I agree about the complexity bit. It feels like there's a much smaller and more elegant language inside of C++ struggling to get out.

    • @fschutt247
      @fschutt247 6 років тому +3

      Well, Rust stores strings as "fat strings", i.e. it doesn't reduce the string down to a pointer, but a pointer + a length. Meanwhile C++ needs the whole std::string class construction, just to store the length alongside with the string and therefore you have to care about these details.
      See: godbolt.org/g/nZma1i for a Rust version - notice that it stores the string length.
      The problem C++ has is this: If you return a const char*, you can only get the length of the string via O(n) counting. If you return a std::string, accessing the string length is now O(1), but you have to go through the std::string constructor, which generates much more assembly. Meanwhile Rust has both: O(1) access to the string length + small assembly.
      Secondly, Rust can annotate that the lifetime of the returned string is a compile-time constant (via 'static). For example, you could do this (stupid, but valid C++):
      const char* get_value() {
      std::string a = "Hello";
      // returning pointer to function-local variable
      return a.c_str();
      }
      or:
      const char* get_value() { return nullptr; }
      versus:
      const char* get_value() {
      // ok - returning pointer to constant variable
      return "Hello";
      }
      Again - it's perfectly possible to replace const char* with std::string in these examples, but then you have the performance penalty of having to go through the std::string constructor, which, even with GCC, generates fairly horrible assembly compared to the const char* version.
      And yes, GCC catches this with -Wreturn-local-addr in these simple cases (in more complex cases it'll fail), but my point was that it is possible to compile this in the first place. To the outsider, who doesn't look at the function content, all functions have the same signature, but two of them will crash.
      Rust can annotate the const char* with a marker ('static) that says: This string is known at compile time and therefore always valid during the duration of the program. Also the pointer can never be nullptr or point to invalid memory. C++ can't make these kinds of guarantees, you are at the mercy of the original programmer.
      Last but not least, Rust also does not have implicit conversions from a const char* to a string, you have to explicitly convert it. In the example in the video, notice how the assembly varied depending just on the return type of the function - it is invisible that there is a std::string construction going on until you look at the assembly. I personally find implicit conversions very dangerous - yes, you save a bit of typing, but it can hide a huge performance bottleneck and lead to bugs.
      Rust is certainly not easier than C++ at first but it's very consistent, there are no huge surprises. So in the long run, I think it's easier to learn than C++. I didn't want to evangelize Rust here, but it's pretty much the only language that comes close to this efficiency. If it has a "better grammar" - well, that's in the eye of the beholder.

  • @Bbdu75yg
    @Bbdu75yg Рік тому

    This is why I hate c++
    So easy to do the wrong thing its driving me crazy