Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

STL

Niner since 2008

  • Stephan T. Lavavej - Core C++, 8 of n

    Ivan> BTW regarding ranges do you feel they should be implicitly convertible to bool.

    C++11 has explicit operator bool(), which is nice and safe, but I don't have a strong opinion about whether ranges/containers should be boolean-testable. I guess it would be reasonable.

    VSChawathe> I was advised by one of my engineering lecturers that do while finds use to handle errors- perform operation and if not error repeat.

    I don't buy that. Such do-while loops can be transformed into while loops by initializing err to success.

    > Thank you for helping me learn C++ renaissance with yourself (including your team).

    You're welcome!

    > I just observed the buffered data with reinterpretation.

    This is bad because it's dependent on your platform's integer widths, layout, endianness, etc. (fread()/fwrite() may encourage it, but they're from a simpler era.) You should use a serialization library instead.

  • Stephan T. Lavavej - Core C++, 8 of n

    Ivan> 1) I remember you (long time ago in one of your A) not being fan of ranges as fundamental element instead of iterators...now they are on their way to std, I wonder if your opinion changed.

    The proposal I've seen treats ranges as pairs of iterators, and I'm a huge fan of that.

    > 2) What features you think will be most beneficial in C++14/17(for a lib developer/for a "normal" dev)

    No major features have been voted in yet (my greater<> functors aren't a major feature), and I haven't attended the Evolution Working Group's meetings. (I live in the Library Working Group.) So I don't have a very good sense of what's coming in the Core Language. I'm also not very familiar with what's happening in Concurrency.

    In the Standard Library, ranges have the potential to simplify nearly every line of code you write using the STL. Portable filesystem and networking libraries will also be important (it's about time the Standard recognized the existence of directories and the Internet).

    I have not seen Boost.Serialization or other serialization libraries proposed for standardization - I hope I'm not forgetting something.

    > 3) In your opinion what is the biggest unforced error in the core language.

    I still don't understand why the pointer-to-member operators .* and ->* have such low precedence; they're lower than . and -> and they're right above multiplication. This forces you to write (obj.*pmf)(args) when calling a pmf. Unless I'm totally missing something, they should be at the same level as . and -> (basically the highest).

    Most of C++'s other problems were inherited from C, so things like the precedence of the bitwise operators, and default fallthrough, don't count. Implicitly converting constructors are widely regarded as the wrong default now, but at the time it was consistent with C's extreme fondness for implicit conversions, and it took time for the problems to become apparent.

    Paul> xy_swap.hpp: //swap definitions dependent on x and y

    What is this swapping? Swap overloads should appear directly after the things they're swapping.

  • Stephan T. Lavavej - Core C++, 8 of n

    CornedBee> Of course, it's very simple to avoid this. Just don't use unnamed namespaces in headers. Ever.

    Yeah, that's why header-only code like Boost uses detail namespaces. (We don't in the STL because we have _Ugly names.)

    > the built-in sizeof... is O(1).

    Good point. (I didn't follow its evolution very closely.)

    > BisectHelper's first specialization is overly verbose. Why not just do this?

    Good catch, I didn't notice that. I am extremely paranoid about setting up an unambiguous system of specializations, but in this case they are cleanly divided by true/false.

    > Concat is also too complicated. Or rather, it is a more complicated primitive than you need.

    Another good catch. I wrote the whole thing in the order presented, and didn't notice that I was using a fraction of Concat's power.

  • Stephan T. Lavavej - Core C++, 8 of n

    jorgito11: Thanks! It amuses me that I was able to quote TC++PL nearly verbatim despite having forgotten where I learned this from.

    Ivan: Yeah, I could spend a whole episode (or several) on variadic templates and how pack expansions work. I probably assumed too much, despite having gone over a simple example in Part 6.

    > Do you have some other functions or idioms you like to write?

    I used to maintain a personal library, but I haven't written a lot of code at home lately. Work is all-consuming.

    > do you have any strong reasons why I shouldnt use this wrapper.

    Seems like a lot of machinery to avoid a little bit of syntax.

    The reason why I think erase-remove should be wrapped is that it's really easy to forget the second end(), in which case it'll compile silently and do the wrong thing.

    > aka this is impossible: > auto it = container_alg(std::find, v, [](const....

    Yeah, you can't pass a function template around like that. (You can if it's a struct with a templated function call operator.)

    The Standard actually has a very special rule here. You *can* pass the name of a function template, but it has to be to a function parameter that provides sufficient type information for the compiler to figure out which overload/instantiation you want. There are entire sections for this, N3485 14.8.2.2 "Deducing template arguments taking the address of a function template" [temp.deduct.funcaddr] and 13.4 "Address of overloaded function" [over.over].

    Nam> Regarding the compile time sorting example, I will be very happy if you could provide some hints for comparing literal strings rather than ints.

    Strings can't be manipulated at compile time like this (except I don't know what constexpr is capable of). String literals are forbidden as template arguments.

    > if we can sort them in compile time then we can minimize searching time by using the binary search algorithm on runtime.

    I would recommend checking is_sorted() once (at startup, possibly in debug mode only) so if the sort order is damaged during maintenance, you'll quickly find out.

    gnzlbg: Macros are evil!

    CornedBee> If the compiler does that, it's buggy. Overload resolution only requires the signature of the function to be instantiated; it's definition won't be instantiated unless the function is actually selected.

    I am aware of this. I was not referring to the function definition.

    > Since these are references (and also function parameters/return values in a prototype), > we don't need the complete type of Bignum<long long>. So only the class declaration is instantiated, not the class definition.

    I could have made a mistake here. I was simplifying the actual example from the STL, which involves a swap for _Vb_reference<Alloc>, and that one takes its objects by value (NOT by reference).

    14.7.1 [temp.inst]/1 "Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program." appears to agree with you.

    I apologize for the confusion. Have you ever thought about applying to work on the compiler team? :->

    > But a C-style cast doesn't do that; it will do the work of both a reinterpret_cast and a const_cast.

    That was in the list of bullet points I showed, although I didn't spend time on it.

    > But it gets even more desperate! It ignores access specifiers in order to achieve its goals.

    I forgot about that, and it's a good point. (I've seen it mentioned before, although it hasn't personally burned me.)

    > To recap, you said that (Derived*)base_ptr is dangerous because, due to a programmer error, Derived might not actually derive from Base, but the C-style cast will happily fall back to a reinterpret_cast in this situation.

    And this was from real-world experience in the STL, not just a theoretical concern.

    > And it can't reverse a hierarchy cast through a virtual base.

    Really good point! (Our offending machinery in <functional> didn't involve virtual inheritance. Actually, virtual inheritance is almost never used in our C++ Standard Library implementation, except where mandated by iostreams, so I don't work with virtual bases very often.)

    That reminds me of a crazy-subtle bug we fixed in weak_ptr. weak_ptr<T> has a "converting copy constructor" from const weak_ptr<Y>&. This implicitly converts Y * to T *. The bug was that our implementation didn't work with virtual inheritance. Implicitly converting from Derived * to Base * always succeeds, but when virtual inheritance is involved, the offset has to be determined at runtime. With weak_ptr, the object could be dead! Therefore, we need to lock weak_ptr<Y> to shared_ptr<Y>. If the object is alive, the shared_ptr will keep it alive while we perform the conversion, and if the object is dead, we'll be informed that it's dead so we can store a null pointer. (We always lock, instead of trying to sense whether the conversion traverses virtual inheritance.)

  • Stephan T. Lavavej - Core C++, 8 of n

    Simon Buchan: Okay, that might be another legitimate use for do-while, although I'd throw a real parser (like Boost.Spirit) at that problem.

    mario: Nothing is wrong with while-loops, although they're less structured than for-loops. I specifically criticized do-while loops, which are very different. The problem with do-while (as I learned from a book whose title I forget) is that on the unconditional first iteration, either the loop invariant as expressed by the condition or something very similar to it must hold in order for the body of the loop to correctly execute. In almost all cases, such loops can be restructured into while-loops while improving clarity.

    Fuz: I would probably use a helper function, but I might also use a lambda. [&] has the advantage of being extremely terse.

    Mark: Are you using precompiled headers?

  • Stephan T. Lavavej - Core C++, 8 of n

    Simon Buchan: Does JSON (which I know virtually nothing about) permit empty arrays?

    xKubo: I prefer to use a for-loop with directory_iterator or recursive_directory_iterator.

    xKubo> What is the reason that the return expressions in STL are in parentheses?

    This is Dinkumware's convention, which I'm careful to follow when hacking their code. My personal style (as you can see in my examples and collection.h) omits parentheses from return statements as they are unnecessary.

    Visitante: Some people use helper functions that static_cast in release and dynamic_cast in debug. In my own programming, I use inheritance rarely and downcasts virtually never, so I haven't used such helper functions.

  • Stephan T. Lavavej - Core C++, 8 of n

    dot_tom> At some point I think it would be incredibly useful if you could share your personal experience and tips for writing additional libraries that play nicely with the Standand library.

    What would you like to know, other than that your code should follow the principles of modern C++?

    rhalbersma> what is your opinion on the use of #pragma once?

    I personally use portable guards, but I'm willing to also use #pragma once as an optimization.

    > I really hate writing include guards, which is especially annoying if refactor a lot and have the file path in the guard's identifier.

    You could just use random hexits.

    Mathias Vorreiter Pedersen: That's another excellent example of when do-while should NOT be used.

    Your example incorrectly handles cases where cin ends without "end" appearing on a line by itself - in particular, the corner case where cin immediately ends (e.g. because an empty file was piped to it, or because the user immediately entered Ctrl-Z).

    A superior way to write this is:

    for (string s; getline(cin, s); ) {
        if (s == "end") {
            break;
        }
    
        // do stuff with s
    }
    

    This consumes all lines from cin without dropping any on the floor or inventing spurious empty lines, but breaks early if it sees "end".

    cmeerw> Is the source code for the variadic template array sorter available somewhere?

    Here it is: http://sdrv.ms/11qJ2OF

    (Sorry, I should have posted it immediately, like I promised.)

  • Stephan T. Lavavej - Core C++, 8 of n

    Thanks for watching, everyone!

    Bruno: I was born with unilateral microphthalmia, a birth defect where my right eye wasn't fully formed. From when I was 6 months old to a couple of years ago, I wore a scleral shell (prosthesis) that gave me a mostly-normal appearance, except that it didn't move and was uncomfortable (like an irritating contact lens, except worse). So I stopped wearing it and switched to my new glasses. This is much more comfortable and people can tell where I'm looking now.

    BillyONeal: That's a perfect example of when do-while should NOT be used. If you initialize error to ERROR_MORE_DATA, you can use a simple while loop.

    Remember, on the first iteration of a do-while loop, the invariant or something very close to it must hold. (I wish I could remember the book where I learned this from.) In your example, the invariant is "we need more data", indicated by error == ERROR_MORE_DATA. On the first iteration, "we need more data" is true, but you've initialized error to 0, so the condition does not hold as written. But because error's value isn't used before the call to SomeCApi() overwrites it, you can easily change your initializer so the condition holds as written, allowing you to use a while loop.

    next_permutation() is different. It simultaneously modifies the sequence, and returns false when it wraps around. Users can't be tricky and start with a sequence sorted in descending order, then call while (next_permutation(v.begin(), v.end())), because that will immediately wrap around and return false. There are other ways to call next_permutation() in a loop, for example for (bool keep_going = true; keep_going; keep_going = next_permutation(v.begin(), v.end())), but they end up being more verbose than do-while.

    Visitante: There are so many ways that explicit template arguments can backfire when a function isn't specifically designed for them. (forward<T>(t) is, and make_shared<T>(args, args, args) is for the first one; note that these are required.)

    Consider what would happen if someone defined a Bignum templated on Allocator, and overloaded max(const Bignum<Allocator>&, const Bignum<Allocator>&). When you say max<long long>, name lookup will find all of the possible maxes that are in scope (you may have a using namespace std; and a using namespace BigMath;). Then because you've provided explicit template arguments, it'll bypass template argument deduction and directly try to substitute them in. The one in std results in max(const long long&, const long long&) which you want. But the compiler will also generate the signature max(const Bignum<long long>&, const Bignum<long long>&) because overload resolution happens *later*. It is quite possible for Bignum<long long> to explode because long long is not an Allocator - that is, merely forming that type can cause a compiler error, even though you aren't calling any Bignum members and the whole overload is nonviable anyways. Such a compiler error does not trigger SFINAE and the whole compilation fails.

    Instead, if you let template argument deduction do its job, it will realize that there is no choice of Allocator that can make Bignum<Allocator> identical to long long, and the overload is silently dropped from consideration.

    I recently solved a related problem with someone saying max<T> in a different context. In that case, they were passing it to a template taking a functor, so they wanted a function pointer. But in C++11, max(const T&, const T&) has been overloaded with max(initializer_list<T>). As soon as we added that overload, max<T> became ambiguous in their context. The solution was to say static_cast<const T& (*)(const T&, const T&)>(max), which allows the compiler to correctly figure out which overload is desired.

  • Stephan T. Lavavej - Core C++, 7 of n

    Part 8: http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-Cpp-8-of-n

  • Stephan T. Lavavej - Core C++, 7 of n

    Alex> is there any approx info about STL update? we want initializer lists!! Smiley

    I can't talk about release dates, sorry.

    soc> I understand why you have the fifth and sixth overloads, but is there a reason you didn't just define the first four as one function using std::numeric_limits' ::lowest and ::max?

    We define find() in our internal header <xutility>, which is included by almost everything and doesn't drag in <limits>.

    Fredrik> Because the compiler could silently do the wrong thing in the background.

    It's a spurious warning. It doesn't lead to silent bad codegen. Nowadays I'd just suppress it with a (void) cast. (I did report it to the compiler team.)

    C++11's minimal allocator interface doesn't require destroy() anymore, which is nice.

See more comments…