1

I've been refreshing/updating my knowledge of C++ lately, and learning about strict aliasing has made me a bit wary of casting pointers of one type to another. I know that this following code sample works in practice on my compiler, but I want to make sure that it conforms to current standards:

#include <iostream>

using namespace std;

class MyBase {

    public:
    virtual void DoSomething() = 0;

};

class MyDerived1 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #1" << endl;
    }

};

class MyDerived2 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #2" << endl;
    }

};

template <typename Base, typename Member1, typename Member2>
struct Tuple {

    public:
    Base* Get(int i) {
        return &(this->*(lookupTable[i]));
    }

    private:
    Member1 member1;
    Member2 member2;

    static Base Tuple::* const lookupTable[2];

};

template <typename Base, typename Member1, typename Member2>
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = {
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1),
    reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2)
};

int main() {

    Tuple<MyBase, MyDerived1, MyDerived2> tuple;

    tuple.Get(0)->DoSomething();
    tuple.Get(1)->DoSomething();

    return 0;

}

Essentially, this simple tuple contains a pair of elements, each of which should derive from a common base class. The Get function returns a Base* to the member that the given index represents.

The key part that I'm wondering about is the reinterpret_casts. I know that casting from Derived Struct::* to Base Struct::* is generally a no-no, but in this case I only use the pointers-to-member-variable to get a pointer to the object. (I don't try to copy a derived object as though it were a base object, nor stuff a base object into a derived object's memory.) This works as intended on G++, and I just want to be sure that I'm not going to get bitten by any compliant compilers for doing this.

nonoitall
  • 1,232
  • 1
  • 15
  • 25

4 Answers4

2

You should not use reinterpret_cast there. Actually you should not mention usage of reinterpret_cast anywhere where your goal is portability. reinterpret_cast is by definition something that has platform-specific results.

For casting a pointer to base into pointer of derived class use dynamic_cast, it will return NULL when the object pointed is not of derived class. If you are absolutely sure that the class is correct then you may use static_cast.

Öö Tiib
  • 10,809
  • 25
  • 44
  • 1
    The outcome of "reinterpret_cast" is not "by definition" platform specific. Nor is it platform specific by any other name. However, it is dangerous because it throws away type-safety. The result can be disastrous, and maintenance of the code precarious. But otherwise, yes, dynamic_cast is what should be used instead. – Brent Arias Jan 03 '11 at 06:10
  • dynamic_cast won't work here because these pointers do not point to objects - they point to relative member offsets. At the point the cast is used, there is no RTTI for a dynamic cast to make use of. static_cast also will not work because the pointer types are technically incompatible. – nonoitall Jan 03 '11 at 06:17
  • 2
    If you read the standard then only case when reinterpret_cast is defined behavior is to cast it back to type from what it was reinterpret_cast before. Rest of the cases are platform-specific. – Öö Tiib Jan 03 '11 at 06:21
  • @Oo Tiib: this is not so. Just because the standard does not prescribe the behaviour specifically does not mean the behaviour isn't well defined. For example, where I abbreviate: `rc(rc(rc))` is clearly well defined. – Yttrill Jan 03 '11 at 07:02
  • @Brent Arias @Yttrill: The C++ standard does say that "the mapping performed by `reinterpret_cast` is implementation-defined" (ISO C++ standard 5.2.10/3), so it is in a way platform specific. – In silico Jan 03 '11 at 07:24
  • @Yttrill: Yes, thanks, to char* from pointers to POD types is special case, but OP did not ask for it. – Öö Tiib Jan 03 '11 at 07:24
1

Use of reinterpret_cast is almost never portable. On top of that, the only valid use of pointer to member casts are the implicit cast from Type Derived::* to Type Base::* and careful uses of the static_cast from Type Base::* to Type Derived::*. Since you want to change the type of the member, not the type of the object containing members, this is neither of those.

How about putting tiny functions in that array instead of pointers to members? The following code is tested and should be entirely portable.

#include <iostream>

using namespace std;

class MyBase {

    public:
    virtual void DoSomething() = 0;

};

class MyDerived1 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #1" << endl;
    }

};

class MyDerived2 : public MyBase {

    public:
    virtual void DoSomething() {
        cout << "I'm #2" << endl;
    }

};

template <typename Base, typename Member1, typename Member2>
struct Tuple {

    public:
    Base* Get(int i) {
        return &(this->*lookupTable[i])();
    }

    private:
    Member1 member1;
    Member2 member2;

    template <typename MemType, MemType Tuple::*member>
    Base& GetMember() { return this->*member; }

    typedef Base& (Tuple::*get_member_func)();
    static const get_member_func lookupTable[2];

};

template <typename Base, typename Member1, typename Member2>
const typename Tuple<Base, Member1, Member2>::get_member_func
Tuple<Base, Member1, Member2>::lookupTable[2] = {
    &Tuple::GetMember<Member1, &Tuple::member1>,
    &Tuple::GetMember<Member2, &Tuple::member2>
};

int main() {

    Tuple<MyBase, MyDerived1, MyDerived2> tuple;

    tuple.Get(0)->DoSomething();
    tuple.Get(1)->DoSomething();

    return 0;

}
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I had this thought too, but my main reason for this convoluted setup is to try and achieve as few function calls as possible. I can do this as a last resort, but I'm hoping someone knows a (compliant) way to achieve this without necessitating a function call. – nonoitall Jan 04 '11 at 00:39
0

EDIT: Reference from standard. If I'm reading it right, as you don't meet either of the exceptions, what you've done is unspecified and so may or may not work on any particular compiler. There aren't any exceptions for the type of the member being related.

From 5.2.10/9 (reinterpret_cast):

An rvalue of type “pointer to member of X of type T1” can be explicitly converted to an rvalue of type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types.66) The null member pointer value (4.11) is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:

— converting an rvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.

— converting an rvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value.

Community
  • 1
  • 1
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • If I chose MyDerived1, then I would have to incorrectly cast a member pointer to MyDerived2 to MyDerived1. The inverse applies as well. – nonoitall Jan 03 '11 at 06:52
-1

C style cast is always better than reinterpret_cast.

If C style cast work, it is valid platform independently.

Always avoid reinterpret_cast.

EDITED

I mean, with reinterpret_cast you could point wrong memory address, C style cast handle all the platform related issues such as ABI, memory align, pointer size, etc.

EDITED By the inspiration of commentators, I've read ISO/IEC 14882:2003 section 5.2.10 "Reinterpret_cast".

Of course my comprehension is limited, but it strikes me to remember why I hated reinterpret_cast in the first place.

I think, reinterpret_cast is lack of or has very limited awareness of inheritance hierarchy.

If cast operand is a instance pointer of class which has complicated inheritance hierarchy (such as ATL/COM classes), one reinterpret_cast is enough to kill you process with incomprehensible errors.

We can use C style cast with vague knowledge of actual cast operation behind. But we must really know exact detail to use reinterpret_cast safely.

9dan
  • 4,222
  • 2
  • 29
  • 44
  • If that's the case, what is the purpose of reinterpret_cast? – nonoitall Jan 03 '11 at 06:53
  • The original idea was to factor the casts to make the intent clearer since C style casts are a bit of a kitchen sink/swiss army knife: you can't even tell the difference between a conversion and a reinterpretation. Unfortunately, the casts in the Standard fail to properly partition the space, and include at least one absurd rule restricting reinterpret cast (when no restriction is logically sane). It's a pity, the idea was good. – Yttrill Jan 03 '11 at 07:05
  • reinterpret_cast is this: "I'm the master! you compiler fall off! I know what I'm doing!". Very dangerous indeed to ordinary people like us. – 9dan Jan 03 '11 at 07:06
  • FYI: apart from occasional dynamic_cast I generally use C style casts, although admittedly I usually generate C++ rather than writing it by hand. – Yttrill Jan 03 '11 at 07:07
  • @9dan: indeed, but then C++ is dangerous, if you're scared of casts you should try a sane language like Ocaml that doesn't need them (15 years of using it, never wrote a cast .. well, there aren't any, so how could I? :) – Yttrill Jan 03 '11 at 07:09
  • So if I change the reinterpret_casts above to C-style casts, things *should* be copacetic with compliant compilers? – nonoitall Jan 03 '11 at 07:18
  • @nonoitall: It might allow for the code to compile, but then there's no guarantee that the code will actually work. – In silico Jan 03 '11 at 07:23
  • IMHO if it compile successfully, there is no reason to things are going wrong. – 9dan Jan 03 '11 at 07:40
  • 1
    -1, entirely wrong. C casts are described in terms of other casts, including `reinterpret_cast<>`. Obviously, that invalidates the claim that it's always better. But it's worse than that. The standard may be explicit which type of C++ cast is used for a C-style cast, but the programmer writing or reading the code may have incorrect assumptions. Therefore, the named C++ casts (including `reinterpret_cast` are strictly not worse (and often better) than the C stule cast. Another reason for the -1: C style cast work are _not_ platform-independent, again because they can map to `reinterpret_cast` – MSalters Jan 03 '11 at 11:02
  • @MSalters What's your point? My point is about to use or not to use reinterpret_cast. Suppose you have two options: C cast and reinterpret_cast. Which one will you choose? – 9dan Jan 03 '11 at 11:16
  • @MSalters I said "If C style cast work, it is valid platform independently". In this question, it is pointer to pointer cast. Am I really wrong? Is there any compilers that compile without errors and doing wrong with pointer to pointer cast?? – 9dan Jan 03 '11 at 11:18
  • @MSalters ah.. "C casts are described in terms of other casts" this is your misunderstanding. C cast can not be described/implemented with C++ cast. Moreover implementation detail of reinterpret_cast is not defined at all. C style cast map to reinterpret_cast?? NO WAY – 9dan Jan 03 '11 at 11:30
  • The C style cast almost always does exactly the same as one or two of the C++ conversions. The reason all C++ programmers should avoid the C style cast is that it's not obvious which of `static_cast`, `reinterpret_cast`, and/or `const_cast` are really happening. – aschepler Jan 03 '11 at 21:11
  • @aschepler Partly true partly wrong. C style cast perform various combination of C++ cast behind the scene. The reinterpret_cast is only a potion of this operation. In odd cases you need to perform various C++ casts and address adjust to accomplish the same effects as C style cast does. – 9dan Jan 04 '11 at 05:12
  • @9dan: "C style cast map to reinterpret_cast?? NO WAY" - better reread 5.4.5 in the standard, or check http://stackoverflow.com/questions/674982/performance-hit-from-c-style-casts/675042#675042 . When I _have_ to choose between a C style cast and a `reinterpret_cast` cast, that's because I have a source type `S` and a destination type `D` that cannot be converted via a `static_cast` or a `dynamic_cast`. (See 5.4.5 again) Therefore, the correct cast is `reinterpret_cast(S_expr)` which makes the relation between `S` and `D` obvious to the readers of the code. – MSalters Jan 04 '11 at 14:06
  • @MSalters if static_cast and dynamic_cast is not applicable, you could try reinterpret_cast. But if you really reinterpret_cast no standard ensure you have usable target type. reinterpret_cast is not guaranteed to return safely usable pointer. So effectively you fall into very dangerous implementation defined area. IMHO, only non-professional C++ programmers use reinterpret_cast in there pet projects. – 9dan Jan 04 '11 at 14:32
  • @aschepler read done. What do you mean with section 5.4.? How about this excerpt from section 5.4. "In addition to those conversions, the following static_cast and reinterpret_cast operations (optionally followed by a const_cast operation) may be performed using the cast notation of explicit type conversion, even if the base class type is not accessible:" ? – 9dan Jan 04 '11 at 14:48