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

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

Download

Right click “Save as…”

In part 8, STL digs into the do-while loop, casts, one definition rule (ODR), and his variadic template array sorter. There is a lot of information in this episode, so get comfortable, tune in, and learn.

See part 1: Name Lookup
See part 2: Template Argument Deduction
See part 3: Overload Resolution
See part 4: Virtual Functions
See part 5: Explicit and Partial Specialization
See part 6: New C++11 features added to the Visual C++ 2012 compiler (CTP)
See part 7: Usual Arithmetic Conversions and Template Metaprogramming

 

Tags:

Follow the Discussion

  • BrunoBruno

    That was awesome, you have a very good public presentation skills !

    Sorry about asking that, but as a curious doctor, why you using a eye cover ?

  • RicodeRicode

    Thank you for the video!

  • Here's another extremely common do-while example -- interfacing with most many C APIs. For instance:

    std::vector<unsigned char> buffer;
    unsigned int bufferLen = 0;
    unsigned int error = 0;
    do
    {
        buffer.resize(bufferLen);
        error = ::SomeCApi(buffer.data(), &bufferLen);
    } while (error == ERROR_MORE_DATA);

  • VisitanteVisitante

    Thank you for the great video!

    You mentioned as an example a templated max-function:
    template<typename T>
    const T& max(const T&, const T&);
    and two values:
    long long ll;
    int i;
    Why is it better to write:
    max(ll, static_cast<long long>(i));
    instead of:
    max<long long>(ll, i);

  • STLSTL

    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.

  • Another awesome episode Stephan, thank you! 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.

  • Day made.

  • rhalbersmarhalbersma

    Really good stuff as always, Stephan!

    Re: include guards, what is your opinion on the use of #pragma once? I really hate writing include guards, which is especially annoying if refactor a lot and have the file path in the guard's identifier. Except for some dinosaur compilers (e.g. Sun) that don't support C++11 anyway, it is supported on all platforms (VC, gcc, Clang, Intel) so there appears to be little or no drawbacks (and yes, I know it's not Standard compliant, but in practice it just works).

  • Mathias Vorreiter PedersenMathias Vorreiter Pedersen

    When introducing people to do-while loops I usually do the following:

    std::string s;
    do {
    std::getline(std::cin, s);
    std::cout << s << std::endl;
    } while(s != "end");

    vs.

    while(true) {
    if(s == "end") {
    break;
    }
    std::getline(std::cin, s);
    std::cout << s << std::endl;
    }

    But I guess it all comes down to your opinion of while true loops. I dislike these since, at first glance, it looks like an infinite loop and I have to inspect the body of the loop to actually detect what kind of condition terminates the loop.
    When doing a do-while loop I know for a fact that the loop termination condition is at the bottom of the loop. (Ignoring the fact that additional break statements could be placed inside the body as well.)

  • cmeerwcmeerw

    Is the source code for the variadic template array sorter available somewhere? (if not, could you make it available?)

  • GarciatGarciat

    Until source gets uploaded by STL: http://liveworkspace.org/code/30Hc9s$0 (includes online compiler for testing)

    I by no means imply ownership of this code. All rights belong to STL. If the source is not supposed to be shareable, please delete this comment.

  • STLSTL

    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.)

  • Simon BuchanSimon Buchan

    I run into do .. while mostly in writing recursive-descendant parsers: for example, reading a JSON array is:

    json_array json_read_array(json_input& input) {
    json_array result;
    input.expect(json_token::array_open);
    do {
    result.push_back(json_read_value(input));
    } while (input.accept(json_token::comma));
    input.expect(json_token::array_close);
    return result;
    }

  • Looks like the High Quality WMV file is broken.... can you fix it?

    Thanks

  • CharlesCharles Welcome Change

    @AlessandroV: What's wrong with it? I downloaded it and it played fine, all the way through.

    C

  • xKuboxKubo

    I found just one use of the do/while loop:

    if (findfirst())
    do {

  • xKuboxKubo

    Sorry, the whole example should have been :

    if (findfirst(...))
    do
    {
    ...
    } while (findnext(...));

    I have yet another question : What is the reason that
    the return expressions in STL are in parentheses?
    E. g. : return (this->_Mylast - this->_Myfirst);

    Thanks for answer.

  • VisitanteVisitante

    I totally agree with you that casts should be avoided. But when there is a good reason to downcast I would prefer dynamic_cast over static_cast. Even if I am absolutely shure that the cast will never fail the former will imediately indicate if I am mistaken while the latter will result in UB.

  • @Charles: Yesterday the playback of the downloaded file stopped around the half, but today I downloaded again and it works fine; maybe it was my internet connection, sorry about the false report.

  • STLSTL

    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.

  • Simon BuchanSimon Buchan

    @STD: Whoops, yeah, I was throwing together a quick example late at night (not real code :P), there should be an "if (!accept(array_close))" before the do .. while. But my point stands in that there's not a natural while loop there (that I can see): tailing commas are not permitted (so no "while (!accept(array_close)) { .. if (!accept(comma)) break; }"), and you want to give explicit errors about missing a ] (or maybe a ,) so it's not natural to share the conditional and the close. This general pattern of token seperated expression lists comes up a bunch, even operator precedence trees can be done as nested do .. whiles.

  • Please to see do-while take some heat, it's a horrible construct I think. I wish to goodness that ATL didn't use things like 'do { /* stuff */ } while(0);' .

    This episode has been an absolute goldmine of goodies, I keep coming back and re-watching it.

  • mariomario

    please, I don't underestand very well the english in the video, could you write the reasons to avoid while and do while loops?
    thanks!

  • Steve RobbFuz

    Oddly enough, today I just added the first do-while loop into our year-old codebase.  It's not something I like to do often, but it's occasionally useful.  It was the do-while-false idiom which some people may not be aware of:

    http://blog.slickedit.com/2007/05/do-while-false/

    The idea is to introduce a non-looping block from which you can break in order to perform simple 'local exception handling' when errors occur.  In my case, I was reading a nested JSON object returned from Facebook and if any part of it wasn't in the format I expected, I wanted to ignore it:

    do {
        auto& picture = json["picture"];
        if (!picture.IsObject())
            break;
    
        auto& pictureData = picture["data"];
        if (!pictureData.IsObject())
            break;
    
        auto& pictureUrl = pictureData["url"];
        if (!pictureUrl.IsString())
            break;
    
        // Do something with pictureUrl.GetString() here
    } while (false);

    The alternatives would be:

    • Actual exceptions - overkill, much uglier, slower and not an option in my exceptions-disabled codebase.
    • Invert the conditions and have increasingly-indented code - ugly and doesn't scale.
    • goto the end of the block - not terrible, but goto is not politically-correct.  Also, I think the block makes the intended scope of the feature clearer.
    • Introducing a function which you can return from early - probably not too bad in this case, but often you need to pass a lot of the context from the original function to make it work.  Also, the function has no real meaning in and of itself - it is only being used to avoid a goto or do-while-break construct.

    do-while-false also has benefits in multi-statement macros:

    http://kernelnewbies.org/FAQ/DoWhile0

  • MarkMark

    Hi Stephen

    This is not a specific question about a lecture but something I think would be useful to a lot of C++ developers, especially those working on larger code bases:

    We have a C++ application (~2 million LOC) which makes heavy use of the STL throughout multiple libraries and many compilation units.

    What we experienced was an exponential growth in compile and link times as our usage of the STL grew.

    I believe we tried a number of different things to fix this, but ended up settling on forward declaring all of the STL templates that we wanted to use in our code. We would then only instantiate the real templates once, inside one of the libraries.

    This dramatically cuts down compile and link times, but obviously requires a huge commitment every time we update the STL implementation used and is not a solution if we wanted to move to the Dinkumware STL which uses nested classes instead of private namespaces for implementation details.

    What is your recommendation for developers who want to be able to use the STL freely but keep compilation time low?

    Thanks

  • STLSTL

    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?

  • The C++ Programming Language, Third Edition, Bjarne Stroustrup

    Section 6.3.3 says, "... In my exprience, the do-statement is a source of errors and confusion. The reason is that its body is always executed once before the condition is evaluated. However, for the body to work correctly, something very much like the condition must hold even the first time through. More often than I would have guessed, I have found that condition not to hold as expected either when the program was first written and tested or later after the code preceeding it has been modified. ... Consequently, I tend to avoid do-statements."

  • IvanIvan

    @STL
    cool video, really liked the mix of entry lvl and high lvl stuff. One small complaint is that IMHO you should have explained ... syntax a bit since it is quite new and expert lvl. :) As a Haskell expert with 10x5min tutorial experience I just look at it as head and tail, but Im not sure if Im right.
    BTW I remember you talking in old old video how one of your first custom functions that you write in a project is erase-remove wrapper.
    1 )Do you have some other functions or idioms you like to write?
    2 ) I ask because Im often quite irritated by if (it!=v.end()) so I thought about writing
    container_find and container_find_if that return boost::optional of container iterator, so usage
    is
    auto it = container_find_if(v, [](const double d){ return d >42.0;})
    if (it) {//use it}
    do you have any strong reasons why I shouldnt use this wrapper. Ignore that it is not standard and that I cant make it work atm. :)
    If you ask why I dont make algorithm a template param- some smart ppl said it cant be done
    aka this is impossible:
    auto it = container_alg(std::find, v, [](const....

  • NamNam

    Regarding the compile time sorting example, I will be very happy if you could provide some hints for comparing literal strings rather than ints. This is because we would sometimes use const string array list in source code to use them on runtime such as finding matching string so some how if we can sort them in compile time then we can minimize searching time by using the binary search algorithm on runtime.

  • gnzlbggnzlbg

    To give some macros a function-like call syntax it is useful to force the writing of a semicolon after the macro. You can use a do while loop: "do { /* stuff */ } while(false)" without the semicolon for that :)

  • , STL wrote

    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.

    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.

    Since only the signature of the function is instantiated, we only need the minimum requirements for the argument types. Basically, we can write the function prototype as instantiated and reason about that:

    const Bignum<long long>& max(const Bignum<long long>&, const Bignum<long long>&);

    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. In other words, since it would be sufficient to write

    class Bignum_long_long;

    if Bignum wasn't a template, but specialized copy&pasted code, the compiler won't write more code than that from the template either. In particular, it won't try to instantiate the definition of the class, and therefore any errors resulting from that (such as attempting to access long long::pointer, which would be likely in the case of an Allocator parameter) cannot happen.

     

    It's a different story if the template in question actually needs to be fully instantiated. For example, if the method had some enable_if check that looked into Bignum:

    typename std::enable_if<is_standard_pointer<typename Bignum<Alloc>::pointer>::value,const Bignum<Alloc>&>::type max(const Bignum<Alloc>&, const Bignum<Alloc>&);

    Now it needs to fully instantiate Bignum's definition even when only instantiating the signature, in order to find out what Bignum<Alloc>::pointer is. Now the compiler may fail.

    But not before.

  • On a different issue, C-style casts are even nastier than you've described. Here's two more nasty things they do.

    First, they are even more desperate than a reinterpret_cast. A reinterpret_cast at least promises to preserve constness on pointer conversions. (You can lose constness by converting to an integer and back, but at least that requires two reinterpret_casts.) But a C-style cast doesn't do that; it will do the work of both a reinterpret_cast and a const_cast.

    But it gets even more desperate! It ignores access specifiers in order to achieve its goals. Imagine you have this:

    class Derived : public NonEmptyBase, private Base {};

    Note the private inheritance. You cannot implicitly cast a Derived* to a Base*, nor can you static_cast a Base* to a Derived*. You can reinterpret_cast them, but that's a bitpattern cast, not a hierarchy cast.

    But a C-style cast will do the hierarchy cast. Yes, that weird thing will actually ignore the fact that the relationship between Derived and Base ought to be invisible, and will do a hierarchy cast instead of falling back to a reinterpret_cast. I'm not sure if that's a good thing or not (at least it kinda works), but it's definitely weird.

     

    Second, the missing hierarchy trap is worse that you described. 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.

    Well, Derived might actually be derived from Base, and this might *still* happen!

    static_cast reverses implicit conversions, with some exceptions. It can't reverse added cv-qualifiers. It can't reverse array and function decay. It can't reverse constant 0 to null pointer conversions. And it can't reverse a number of boolean conversions.

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

    class Base {}; class Derived : public virtual Base {};
    Base* pb = new Derived(); // valid
    Derived* pd = static_cast<Derived*>(pb); // doesn't compile
    Derived* pd2 = (Derived*)pb; // reinterprets!

    The reason is that in the object layout of virtual inheritance, given a Derived*, I can find the Base subobject via either following a pointer in the Derived*, or adding an offset specified in the vtable to the this pointer. (Depending on the implementation.) But finding the Derived object in the actual object given only a Base*, I cannot do the reverse. The vtable for Base doesn't contain an offset to the Derived superobject, because Base might not have a Derived superobject. So I have to invoke the full dynamic_cast machinery anyway, just to find out where the Derived superobject lives.

    Which can fail at runtime. static_cast must not fail at runtime. It must be simple and cheap. So it fails at compile time instead.

    Whereas the C-style cast just moves on and does a reinterpret_cast, which is most definitely not the right thing.

    This means that, if you have a C-style hierarchy cast, you can turn your program from well-defined to silently and nastily undefined simply by adding "virtual" to some class definition. If you had used static_cast, at least it would only fail to compile, and you could use a dynamic_cast there.

    Now don't you wish you hadn't used the C-style cast?

  • STLSTL

    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.)

  • , STL wrote

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

    Yes, but I didn't particularly want to leave Vienna. Zurich, where I am now, is already quite far in my opinion. (Yeah, I'm rather attached to my home city.)

  • OK, finished watching the lecture.

    On the ODR rule: here's another fun thing you can do wrong in header files: unnamed namespaces. The common wisdom is not to use them in header files, and there are two reasons for that. The underlying problem is that an unnamed namespace internally gets a name that is unique not to the file it is in, but the translation unit. This means that when included from foo.cpp, an unnamed namespace in a header gets one name, and when included from bar.cpp, it gets another.

    One problem with that is the duplication of everything in that namespace. The symbols are no longer the same, and thus won't be thrown out by the linker. This wastes space, and can be extremely confusing when you define variables there - every translation unit gets its own global variable that the other TUs cannot access. But the behavior, while confusing, is still defined.

    But then you might add an inline function in that header that accesses something in the unnamed namespace. And now you're in trouble with the standard. Here's some actual code:

    namespace {
      struct X { int y; };
    }
    
    inline int foo(int i) {
      X x = { i };
      return x.i;
    }

    Looks harmless enough, right? The inline function is defined in the header, so obviously it's going to be the same in all translation units. Actually, no. When included from foo.cpp, there's a foo that references <foo-unnamed-namespace>::X, whereas when included from bar.cpp, there's a foo that references <bar-unnamed-namespace>::X. So these two are actually different, even though they are textually equal. ODR is violated, no diagnostic required.

     

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

  • And finally, some comments on the sorter. Looks like a fun exercise.

    sizeof... is not just syntactic sugar, though. A manually implemented sizeof... would have linear "runtime" (number of instantiations), whereas the built-in sizeof... is O(1). (One big issue of variadics as they are is that compilation tends to be very slow, due to lack of random access into arguments packs. Some guy (I think from Boost) studied this and found that compilation speed of Boost.Tuple (a preprorcessor-powered non-variadic tuple) compiled significantly faster than a naive variadics-based version.)

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

    template <typename Ints1, typename Ints2> struct BisectHelper<Ints1, Ints2, true> {
      typedef Ints1 first;
      typedef Ints2 second;
    };

    Or even put the typedefs in the primary template and static_assert that Done == true, to capture implementation bugs. The only thing that the verbose version you have gives you is detecting when somebody instantiates BisectHelper with something other than a Ints specialization. Is that worth it?

    Concat is also too complicated. Or rather, it is a more complicated primitive than you need. The only place where you use Concat is in MergeHelper, where you use it as Concat<Ints<N>, something>, where N is a single int. In other words, you never give more than a single int as the first parameter to Concat. Why not save yourself the Ints instantiations and use a Prepend primitive instead?

    template <int A, typename Ints> struct Prepend {};template <int A, int... Vals> struct Prepend<A, Ints<Vals...>> { typedef Ints<A, Vals...> type; };

  • STLSTL

    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.

  • IvanIvan

    @STL
    tnx for the answers. Sorry to hear you are very busy with your work, but at least you are doing STL and not you know maintaining COBOL :) code while hacking with c++ in free time. :D
    BTW I would like to get your quick opinion on couple of things
    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.
    2) What features you think will be most beneficial in C++14/17(for a lib developer/for a "normal" dev)... I know it is not clear what will get in, but you probably know the usual suspects... static if, await(or how is it gonna be called)... me personally would really like efficient serialization.
    3) In your opinion what is the biggest unforced error in the core language. Unforced error here means (like Alexandrescu puts it :)) design error that wasnt necessary(for eg to make C++ compatible with C)

    @CornedBee tnx for the cool comments :)

  • PaulPaul

    I think the point CornedBee raised regarding the ODR is worth expanding on. Consider the following example:

    x.hpp: struct x {...};
    y.hpp: struct y {...};
    xy_swap.hpp: //swap definitions dependent on x and y

    If x or y is ever visible in a translation unit without xy_swap.hpp, in a context where a provided definition may conceivably apply, the code may violate the ODR indirectly through instantiated templates like std::sort.

  • STLSTL

    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.

  • PaulPaul

    That example was fairly contrived I've never seen it occur personally, but it might seem reasonable to a programmer to place the definition in a separate file if it somehow depended on both structures. I was really just pointing out that it's possible for a template to implicitly violate the ODR through templates like std::sort even if the template itself never directly violates the rule, similar to how ConredBee's example violates the ODR with the user's template taking the role of X (conceptually). Non-intrusive serialization is the only place I've seen this occur in practice.

    Perhaps it isn't worth mentioning, but in my experience it can lead to some truly horrific bugs :)

  • IvanIvan

    @STL
    again tnx for answers...
    I was talking about serialization in this sense:
    SG7, Reflection: Chandler Carruth (Google). Initially focusing on compile-time reflection capabilities.
    IDK if reflection will enable it.
    BTW regarding ranges do you feel they should be implicitly convertible to bool.
    From what I see from boost they have .empty() function:
    http://www.boost.org/doc/libs/1_53_0/libs/range/doc/html/range/reference/concept_implementation/semantics/functions.html
    I would like to be able to write if (r)

  • > 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.

    It makes parsers more complicated to mix binary operator and unary operator precedences.I suspect this was the main reason.

    It would so be worth it, though. I think it would work, too. Of course, it's way too late to change it now.

  • Vipul S. ChawatheVSChawathe Vipul

    I was advised by one of my engineering lecturers that 

    do while
    finds use to handle errors- perform operation and if not error repeat. Individually, although I've forgotten when I learnt 
    repeat .. until
    statement (was that Pascal?), I remember using the post-condition evaluation form of loop for receiving messages, mostly by user action, where the message would cause branching by 
    switch .. case
    and instead of using 
    break
    for 
    case '0'
    or 
    Exit
    I'd have 
    while (choice)
    or 
    UNTIL (NOT msg)
     as the post-condition for looping. The compilers back then were slow and as newbie with small datasets this somehow appealed more than reading message once more before entering an infinite pre-condition evaluation loop and asking for compiler optimization. If I've to write message loop for actor/bot/agent in some swarm, I guess I'll default to do-while, unless something smarter occurs to me.

    On separate note, I'd write code like 

    cout<<a+b;
    rather than 
    c=a+b; cout<<c;
    until you demonstrated 
    &&
    and move semantics. Mainly owing to UI, I was doing multi-lingual .net code in my projects but always found that OO was over-rated everywhere. F# for data modeling has been nice experience, and kept reminding me of functors and STL. Now that DirectX & XAML combined apps work, and having academics wrapped up behind me, I'm concentrating on how I can isolate my app's core logic using aspects, so my team's non-Windows members can re-use the platform-neutral aspects when re-implementing the app on other platforms. Thank you for helping me learn C++ renaissance with yourself (including your team). I put on-hold my objective of aspect-oriented architectural design to come to speed with you, and will update on specific advances soon enough, now that I've almost caught up. For now only insight I'm able to frame properly for understanding is I identify aspects as domain-specific, where domain may be problem domain such as social sharing or inventory control, or platform domain such as .net or Windows.

    I never grew up from C-style cast, so the points you made scored. Most notably I realized my only recollection of casting was with the intention of reinterpret_cast (btw, that should be r..._cat, meow & purr Big Smile) Reinterpret, of all the casts because after opening text file as binary, my intent was to get data trying different encodings. Rather than reopening same file multiple times assuming different encodings, I just observed the buffered data with reinterpretation. There are high-level libraries with great performance for such operations, I know. But while studying storage data structures the objective was imagining how debugger or driver can respond with multiple FAST representations to users.

  • STLSTL

    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.

  • Vipul S. ChawatheVSChawathe Vipul

    @STL: >I don't buy that

    I didn't buy that either, cause it wasn't backed up by any case study so seemed more like trivial comment, but upon revisiting the loop construct in this video my unanswered why made me bring that up.

    > This is bad

    I agree, as a student heterogeneous platforms weren't of any concern, but that still doesn't make it good idea. I'm weary of code snippets ending up in unintended places now and invest more time on separation of concerns.

    I've got request. As I've always enjoyed your live demo-ing every point you touch upon, please cover C++ concurrency introduced in http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Herb-Sutter-Concurrency-and-Parallelism before completing the core language only series. Just requesting, nothing hard & fast. That talk was gr8 too, just that multiple independent impressions make learning faster for me.

  • IvanIvan

    @STL
    thank you for the answers... hope that you soon find some other interesting topic. And TBH it doesnt have to be really interesting topic, the fact is that you present a topic in really almost academic way and at the same time it is not dry.

  • czmczm
    Hi STL Thanks for the great talk, especially the compile-time sorter part! I am actively seeking all the available resources and talks at web on C++11. During my daily work, I often deal with kind of "low-level" operations like binary interchange file I/O (like messagepack, TIFF image format). I find "alignment" and "POD" are two topics which I want to understand more in details. Could you please kindly include some materials about those stuff in your future talks? Multi-threading support is another shining piece in C++11. Do you have any interest to touch a bit on that?
  • czmczm
    Hi STL Thanks for the great talk, especially the compile-time sorter part! I am actively seeking all the available resources and talks at web on C++11. During my daily work, I often deal with kind of "low-level" operations like binary interchange file I/O (like messagepack, TIFF image format). I find "alignment" and "POD" are two topics which I want to understand more in details. Could you please kindly include some materials about those stuff in your future talks? Multi-threading support is another shining piece in C++11. Do you have any interest to touch a bit on that?
  • czmczm
    Hi STL Thanks for the great talk, especially the compile-time sorter part! I am actively seeking all the available resources and talks at web on C++11. During my daily work, I often deal with kind of "low-level" operations like binary interchange file I/O (like messagepack, TIFF image format). I find "alignment" and "POD" are two topics which I want to understand more in details. Could you please kindly include some materials about those stuff in your future talks? Multi-threading support is another shining piece in C++11. Do you have any interest to touch a bit on that?
  • Laurent Bourgault RoyLaurent Bourgault Roy

    Merge sort implemented only in templates? Woah, mind = blown.

    I wonder if it would be possible to create an ORM mapper with templates. It would need I guess some sort of syntax to expand a class in its various fields.

  • mloskotmloskot Mateusz Łoskot

    Stephan,

    Perhaps you have touched that in your videos and I've missed it, regarding your std::max with long long and int example: why not explicit type conversion using functional notation instead of, longer, static_cast? I wonder, are there significant differences?

     

    Mat

  • Functional notation is, sadly, semantically equivalent to a C-style cast, with all the dangers that brings. So while it looks nice, it's not a good idea to do it.

    In my opinion, functional casts should be static_casts by semantics, but unfortunately we're fixed in a world where that isn't the case. I suspect that functional cast notation is older than the limited C++ casts, and by the time the verbose syntax was introduced, it was already too late to change the meaning of functional cast notation.

  • I'm looking forward to the next lecture from STL, anyone know when it might be.
    Charles do you know?

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.