Enlightened



  • @Medinoc said in Enlightened:

    It's still highly codesmelly because it forces the data to be saved in host endianness, rather than specifying one.

    I was just pointing out that streams can be used to read and write in binary, you just need to use the right methods (read and write instead of formatted input and output operators), which given an arbitrary address will read or write as many bytes as told to. If you want to add encoding on top of a binary stream of bytes, then you have to pass the stream to the appropriate function for doing that.

    Imagine that snippet is inside a function which statically checks host endianness, and either copies directly if it matches the target endianness, or creates a buffer, copies the bytes in the right order, and then writes that. In the trivial case, the code will pretty much look like that.

    @Steve_The_Cynic said in Enlightened:

    I would normally prefer an implicit cast to void *, or maybe static_cast<void *>.

    reinterpret_cast<T*> is equivalent to static_cast<T*>(static_cast<void*>(ptr))1. Since the read interface took a pointer to char, and you can't implicitly cast from void* to anything else, you would have had to do static_cast<char*>(static_cast<void*>(&myInt)). Which is equivalent to reinterpret_cast<char*>(&myInt). The reinterpret cast makes the intent more clear, I feel.

    This means that reinterpret_cast<void*>(ptr) is a bit redundant, since it is equivalent to static_cast<void*>(static_cast<void*>(ptr)).

    1: relevant bit

    1. Any pointer to object of type T1 can be converted to pointer to object of another type cv T2. This is exactly equivalent to static_cast<cv T2*>(static_cast<cv void*>(expression))


  • @Kian said in Enlightened:

    reinterpret_cast<T*> is equivalent to static_cast<T*>(static_cast<void*>(ptr))1. Since the read interface took a pointer to char, and you can't implicitly cast from void* to anything else, you would have had to do static_cast<char*>(static_cast<void*>(&myInt)). Which is equivalent to reinterpret_cast<char*>(&myInt). The reinterpret cast makes the intent more clear, I feel.

    Sure, but you'll note that in my original post on the subject, I did say that you might have to use reinterpret_cast (not necessarily in a "there's no equivalent mechanism" sense, but perhaps in a "the equivalent mechanism is even smellier-looking" sense).

    And it's an indication that there is a potential issue. It is not an indication that there is definitely an issue and that the program will unleash nose-cratering demons.



  • @Steve_The_Cynic said in Enlightened:

    get from a derived class pointer to a base class pointer

    Isn't that built-in behavior, though?
    I mean, if you really need to call the base-class's version of a function instead of the derived class's override, you're :doing_it_wrong: somewhere in the inheritance scheme.



  • @djls45 said in Enlightened:

    @Steve_The_Cynic said in Enlightened:

    get from a derived class pointer to a base class pointer

    Isn't that built-in behavior, though?

    No - C++ has multiple inheritance, and so you can't simply change your interpretation of the pointer because a base class may have been inherited multiple times and thus have multiple copies. You have to cast through the specific tree branch first, and you'll get different pointer values.

    @djls45 said in Enlightened:

    I mean, if you really need to call the base-class's version of a function instead of the derived class's override, you're :doing_it_wrong: somewhere in the inheritance scheme.

    It's perfectly reasonable to need to do this, especially within the overriding function. C++ even has special syntax for it: instance.Base::function(); instead of instance.function();. Typically you ask the base class to do their usual thing and then you do your thing before or after.



  • @LB_ said in Enlightened:

    No - C++ has multiple inheritance, and so you can't simply change your interpretation of the pointer because a base class may have been inherited multiple times and thus have multiple copies. You have to cast through the specific tree branch first, and you'll get different pointer values.

    @whargarbl



  • @LB_ said in Enlightened:

    No - C++ has multiple inheritance, and so you can't simply change your interpretation of the pointer because a base class may have been inherited multiple times and thus have multiple copies. You have to cast through the specific tree branch first, and you'll get different pointer values.

    Assuming that that class hierarchy in questions does inherit multiple times (non-virtually) from a single class. Otherwise pointers and references to derived types are implicitly convertible to any of their base types (as there is no ambiguity).

    (If you end up with multiple copies of a base class in a single hierarchy ... why are you doing that again?)


  • Winner of the 2016 Presidential Election

    This post is deleted!


  • @cvi said in Enlightened:

    @LB_ said in Enlightened:

    No - C++ has multiple inheritance, and so you can't simply change your interpretation of the pointer because a base class may have been inherited multiple times and thus have multiple copies. You have to cast through the specific tree branch first, and you'll get different pointer values.

    Assuming that that class hierarchy in questions does inherit multiple times (non-virtually) from a single class. Otherwise pointers and references to derived types are implicitly convertible to any of their base types (as there is no ambiguity).

    (If you end up with multiple copies of a base class in a single hierarchy ... why are you doing that again?)

    Implicitly, yes. My statement, however, was that you mustn't reinterpret_cast to do it, because the base class might not be at the beginning of the derived class, regardless of any question of multiple inheritance. (Example: a base class that has no virtual member functions and the class that derives from it does have them. Where is the vtable pointer, if that's how virtual functions are implemented on your compiler? Might it be at the beginning of the derived class, followed by the base class part then the derived class's overt members?)


  • Discourse touched me in a no-no place

    @Steve_The_Cynic said in Enlightened:

    (Example: a base class that has no virtual member functions and the class that derives from it does have them. Where is the vtable pointer, if that's how virtual functions are implemented on your compiler? Might it be at the beginning of the derived class, followed by the base class part then the derived class's overt members?)

    It's things like this that make me appreciate how Java and C# handle this better. :laughing:



  • @Steve_The_Cynic Yes, that's true - reinterpret_cast will produce incorrect results in this case. (IMO - reinterpret_cast should be a last resort anyway, if neither static_cast, const_cast or dynamic_cast apply. A well designed API should require few if any casts, and definitively no reinterpret_casts.)



  • @cvi said in Enlightened:

    (If you end up with multiple copies of a base class in a single hierarchy ... why are you doing that again?)

    The obvious one is IUnknown, of course. My top-level C++ (D)COM object code has several IThingyWhatsit interfaces and they all derive non-virtually from IUnknown so there will be several copies of IUnknown's vtable pointer (updated by each of the derived interface classes and also by the top-level object itself) in the top-level object.

    Yes, the IUnknown "class" has no data members and only "pure" virtual member functions, but that vtable pointer means it has a non-zero size. (And even a totally empty class/struct has a non-zero size. Given class EmptyClass {};, sizeof(EmptyClass) == 1 by standardesque fiat.)



  • @dkf said in Enlightened:

    @Steve_The_Cynic said in Enlightened:

    (Example: a base class that has no virtual member functions and the class that derives from it does have them. Where is the vtable pointer, if that's how virtual functions are implemented on your compiler? Might it be at the beginning of the derived class, followed by the base class part then the derived class's overt members?)

    It's things like this that make me appreciate how Java and C# handle this better. :laughing:

    Sure, but in the end, all I'm saying is that you should not be messing with this kind of detail, and of course reinterpret_cast will almost always (except in the specific type of case cited) end up messing with pretty much any kind of detail.



  • @dkf said in Enlightened:

    It's things like this that make me appreciate how Java and C# handle this better.

    "Not allowing it" isn't really a way of handling something. It's more like saying "I don't want to deal with that, so you can't have it."

    Whether there is much value in allowing it in the first place is a different matter.



  • @Steve_The_Cynic said in Enlightened:

    The obvious one is IUnknown, of course. My top-level C++ (D)COM object code has several IThingyWhatsit interfaces and they all derive non-virtually from IUnknown so there will be several copies of IUnknown's vtable pointer (updated by each of the derived interface classes and also by the top-level object itself) in the top-level object.

    Hence the important notion of "the controlling IUnknown" that's used as the definitive address of an object; and the fact you can't compare two COM object references without asking each for its controlling IUknown first (which is what IsEqualObject does).

    C++ does have a similar mechanism, but it's used only for dynamic_cast, which contributes to make unnecessary reinterpret_cast usage a major no-no.



  • @Steve_The_Cynic Fair enough -- I was more or less expecting somebody to come up with a reason to do so ;-). I didn't consider it, because I've only had to mess with COM stuff on a few occasions (and always on the receiving end, i.e., as a user of the interfaces).

    (EmptyClass is eligible for empty base class optimization though, so if you inherit it, it won't increase the size of the inheriting object.)



  • @Medinoc said in Enlightened:

    C++ does have a similar mechanism, but it's used only for dynamic_cast, which contributes to make unnecessary reinterpret_cast usage a major no-no.

    It does indeed. dynamic_cast<void *>, which I've used in exactly one distinctly dubious context. I think it avoided UB, but only just.




  • Discourse touched me in a no-no place

    @NeighborhoodButcher To be fair, the article indicates that it could affect other brands as well.


  • Discourse touched me in a no-no place

    @cvi said in Enlightened:

    I was more or less expecting somebody to come up with a reason to do so

    The major reason for supporting reinterpret_cast is that it's allowed to convert between pointers and std::uintptr_t. That's vital if you want to write the core of a memory allocator; the real operating system API works with addresses, not actually pointers. There are good reasons for doing that; the standard memory allocator has some tricky down-sides as it is tuned to handle some cases at the expense of others (threaded performance is one of the very very tricky things where tuning code to make it good for some usage patterns makes it terrible for others).

    Yes, memory allocator cores aren't standard things; they have to do things that the baseline language standard doesn't say much about (e.g., interacting with the OS memory manager) but that's because they have to be practical to be useful. And nobody writes memory managers to be totally platform agnostic; there's literally no value in that. But it's still useful to have the language spec allow as much clarity as possible when doing the code.



  • @dkf I was referring to the having multiple copies of a base class in a single hierarchy in that post.

    reinterpret_casting pointers to uintptr_t and back is indeed useful, but it's not something that an API should require its users to do. (Doing it in the guts of your program/library is OK, assuming you have a reason for doing so.)




Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.