Prepare to be horrified

Challenge: see if you can figure out why it crashes. ;p
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <iostream>
#include <memory>
#include <map>
#include <typeinfo>
#include <typeindex>
#include <string>

struct Base
{
    Base()
    {
    }
    Base Clone() const
    {
        Base b;
        b = *this;
        return b;
    }
    Base &operator=(Base const&d)
    {
        this->~Base();
        d.ConstructInPlace(this);
        data = d.data;
        return *this;
    }
    Base &operator=(Base &&d)
    {
        Base &&t = Clone();
        this->~Base();
        d.ConstructInPlace(this);
        data = std::move(d.data);
        d.~Base();
        t.ConstructInPlace(&d);
        d.data = std::move(t.data);
        return *this;
    }
    virtual ~Base()
    {
    }

    virtual std::ostream &Print(std::ostream &os)
    {
        return os << "I'm just a Base";
    }

    Base(Base const &) = default;
    Base(Base &&) = default;

protected:
    template<typename Class, typename T>
    T *&Data()
    {
        return reinterpret_cast<T *&>(data[typeid(Class)]);
    }

private:
    std::map<std::type_index, void *> data;

    virtual void ConstructInPlace(Base *p) const
    {
        new (p) Base;
    }
};

struct Derived1 : Base
{
    Derived1();
    Derived1(std::string const&);
    virtual ~Derived1();

    virtual std::ostream &Print(std::ostream &);

    Derived1(Derived1 const &) = default;
    Derived1(Derived1 &&) = default;

private:
    struct Impl;

    virtual void ConstructInPlace(Base *p) const
    {
        new (p) Derived1;
    }
};

struct Derived2 : Derived1
{
    Derived2();
    Derived2(std::string const&);
    Derived2(std::string const&, double);
    virtual ~Derived2();

    virtual std::ostream &Print(std::ostream &);

    Derived2(Derived2 const &) = default;
    Derived2(Derived2 &&) = default;

private:
    struct Impl;

    virtual void ConstructInPlace(Base *p) const
    {
        new (p) Derived2;
    }
};

int main()
{
    Base ba, bb;
    Derived1 d1 ("heart attack");
    Derived2 d2 ("*facepalm*", 1.23456789);
    auto display = [&]
    {
        ba.Print(std::cout << "ba: ") << std::endl;
        bb.Print(std::cout << "bb: ") << std::endl;
        d1.Print(std::cout << "d1: ") << std::endl;
        d2.Print(std::cout << "d2: ") << std::endl;
    };
    
    display();
    
    ba = d1;
    bb = d2;
    std::swap<Base>(d1, d2);
    
    display();
    
    {
        Base b;
        reinterpret_cast<Base &>(d1) = b;
        reinterpret_cast<Base &>(d2) = b;
    }
    
    display();
}

struct Derived1::Impl
{
    std::string s;
    Impl(std::string const&s) : s(s) {}
};
Derived1::Derived1() : Base()
{
    Data<Derived1, Impl>() = new Impl("default");
}
Derived1::Derived1(std::string const&s) : Base()
{
    Data<Derived1, Impl>() = new Impl(s);
}
Derived1::~Derived1()
{
    delete Data<Derived1, Impl>();
}
std::ostream &Derived1::Print(std::ostream &os)
{
    os << "I'm a Derived1, s = \"" << Data<Derived1, Impl>()->s << "\", dad says ";
    return Base::Print(os);
}

struct Derived2::Impl
{
    double d;
    Impl(double d) : d(d) {}
};
Derived2::Derived2() : Derived1("from Derived2")
{
    Data<Derived2, Impl>() = new Impl(7.42);
}
Derived2::Derived2(std::string const&s) : Derived1(s)
{
    Data<Derived2, Impl>() = new Impl(42.7);
}
Derived2::Derived2(std::string const&s, double d) : Derived1(s)
{
    Data<Derived2, Impl>() = new Impl(d);
}
Derived2::~Derived2()
{
    delete Data<Derived2, Impl>();
}
std::ostream &Derived2::Print(std::ostream &os)
{
    os << "I'm a Derived2, d = " << Data<Derived2, Impl>()->d << ", mom says ";
    return Derived1::Print(os);
}
Did you have a heart attack yet?
closed account (D80DSL3A)
LB wrote:
Did you have a heart attack yet?

Not exactly. I stopped reading at line 21.
Haha, I thought most of you would.

You'd be surprised though - as long as the class sizes are the same (e.g. sizeof(Base) == sizeof(Derived)), you can do some...questionable stuff and the implementation won't care:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
 
struct Base
{
    virtual ~Base(){}
    virtual void Print() const
    {
        std::cout << "Base" << std::endl;
    }
};
 
struct Derived : Base
{
    virtual ~Derived(){}
    virtual void Print() const
    {
        std::cout << "Derived" << std::endl;
    }
};
 
int main()
{
    Base *r1 = new Base;
    Base *r2 = new Derived;
 
    r1->Print();
    r2->Print();
 
    r1->~Base();
    r2->~Base();
    new ((void*)r1) Derived;
    new ((void*)r2) Base;
 
    r1->Print();
    r2->Print();
    
    delete r1;
    delete r2;
}
Base
Derived
Derived
Base
http://ideone.com/HPCIAT
Last edited on
isn't the code more readable if you would use "union" from the beginning? same with reinterpret_cast. really strange how some people claim that "union" is the most useless element of the c++ language and then such programs like above get written. is it still a c++ program if you keep on disabling all the strong type-checking? IMHO union is only useless in c-programs, but in c++ it serves as partial restoration of otherwise destroyed type-checking. my usual approach is to eliminate all reinterpret_cast before going release, and of course all c-style casts get transformed into the correct kind of templated cast already during programming. I see reinterpret_cast as a crutch that must be thrown away...

btw, for me above program doesn't crash, it just outputs in the last line "bb: I'm a Derived2, d = 0, mom says I'm a Derived1, s = " and some garbage (it's the 3rd call to display()). is my gcc too old for a crash? or am I just lucky the string had some zero nearby to avoid segfault?
Last edited on
piotr5 wrote:
isn't the code more readable if you would use "union" from the beginning?
Unions don't support inheritance or polymorphism, what makes you think this example is more clear with them?
piotr5 wrote:
same with reinterpret_cast.
I have to use that daily for an SDK, but I don't see where it would be used here.
piotr5 wrote:
really strange how some people claim that "union" is the most useless element of the c++ language and then such programs like above get written.
Unions are not useless, they are just seldom used. They are still frequently used in C. The above prgrams A. are not supposed to be written ever and B. have nothing to do with unions or your strange fetish with unions.
piotr5 wrote:
is it still a c++ program if you keep on disabling all the strong type-checking?
If it is written in standard C++, yes. But obviously it's not a good thing to do.
piotr5 wrote:
IMHO union is only useless in c-programs, but in c++ it serves as partial restoration of otherwise destroyed type-checking.
Now you're just trolling. Unions are most useful in C, they are much less useful in C++.
piotr5 wrote:
my usual approach is to eliminate all reinterpret_cast before going release, and of course all c-style casts get transformed into the correct kind of templated cast already during programming.
That's quite a sudden change in subject, have you slept recently?
piotr5 wrote:
I see reinterpret_cast as a crutch that must be thrown away...
It is a necessity in rare cases, just put it in your storage shed, don't throw it away completely.
piotr5 wrote:
btw, for me above program doesn't crash, it just outputs in the last line "bb: I'm a Derived2, d = 0, mom says I'm a Derived1, s = " and some garbage (it's the 3rd call to display()). is my gcc too old for a crash? or am I just lucky the string had some zero nearby to avoid segfault?
Haha, that's odd. It crashes with gcc when I tested. It's definitely crashtastic.

Now you're just trolling. Unions are most useful in C, they are much less useful in C++.


What does C++ offer as a C union replacement? IMHO all the C unions would translate to C++ unions, so I don't get how they xcan be much less useful in C++.
Clone() calls operator=(), which calls Clone(), ad crashium.
Also, a Clone() doesn't work properly if it's implemented in the base. It should be a pure virtual implemented in the non-abstract classes.
Also also, it's supposed to return a pointer, otherwise it doesn't use polymorphism and it's just a named copy constructor that only copies the base class.

Your code is bad and etc.
Last edited on
helios wrote:
Clone() calls operator=(), which calls Clone(), ad crashium.
Ah, that explains it - I was trying to do something insane with copy elision.
helios wrote:
Also, a Clone() doesn't work properly if it's implemented in the base. It should be a pure virtual implemented in the non-abstract classes.
I know, this was meant to be horrifying code :)
helios wrote:
Also also, it's supposed to return a pointer, otherwise it doesn't use polymorphism and it's just a named copy constructor that only copies the base class.
I really don't understand copy elision X) but yes I know the usual implementation of the clone pattern, I was messing around here ;)
helios wrote:
Your code is bad and etc.
Why, thank you! :D (seriously I was trying to go for bad code)
> Clone() calls operator=(), which calls Clone(), ad crashium.
`Clone()' calls `operator=(const Base&)' that does not call `Clone()'
(gcc 4.8.0)
I see possible memory corruption due to the horrible "ConstructInPlace" function being virtual.

1
2
3
4
Base b;
Derived1 d;

b = d;  // memory corruption 


This will construct a Derived1 object at &b... but the memory at &b is only large enough to contain a Base.
@Disch tell me what std::cout << sizeof(Base) << ' ' << sizeof(Derived1); is
It depends on the implementation.

The only thing that's certain is that sizeof(Derived1) will be >= sizeof(Base)
Last edited on
I guess it makes sense that an implementation is allowed to make derived classes larger and larger, but I'm in luck:

http://ideone.com/GLb0hb

The evil scale of the code just went up by about 2 notches :)
Topic archived. No new replies allowed.