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.


Log in to reply
 

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