Using a union with unique_ptr - c++

Trying to use a unique_ptr inside a union gives me a segfault when I try to std::move or std::make_unique it.
#include <iostream>
#include <memory>
union myUnion{
struct{std::unique_ptr<float> upFloat;}structUpFloat;
struct{std::unique_ptr<int> upInt;}structUpInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structUpFloat.upFloat = std::move(upF);
bStruct.num.structUpInt.upInt = std::move(upI);
std::cout << "aStruct float = " << *aStruct.num.structUpFloat.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structUpInt.upInt << std::endl;
return 0;
}
However, using a normal pointer works as expected:
#include <iostream>
#include <memory>
union myUnion{
struct{float *pFloat;}structPFloat;
struct{int *pInt;}structPInt;
myUnion(){}
~myUnion(){}
};
struct myStruct{
int x;
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.x = 1;
bStruct.x = 2;
auto upF = std::make_unique<float>(3.14);
auto upI = std::make_unique<int>(3);
aStruct.num.structPFloat.pFloat = upF.get();
bStruct.num.structPInt.pInt = upI.get();
std::cout << "aStruct float = " << *aStruct.num.structPFloat.pFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.structPInt.pInt << std::endl;
return 0;
}
This is using clang.3.4.2 or gcc.4.9.0. So I'm assuming that I am doing something wrong here. Any help would be appreciated.
EDIT:
Ok, so it's probably a nice thing to do to share the code I settled on. Big thanks to everyone who pointed me to managing the lifetime of my pointers in variant members using placement new.
#include <memory>
#include <iostream>
#include <vector>
struct myStruct
{
public:
union
{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
};
enum class unionType {f, i,none} type = unionType::none; // Keep it sane
myStruct(){}
myStruct(std::unique_ptr<float> p)
{
new (&upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
myStruct(std::unique_ptr<int> p)
{
new (&upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
~myStruct()
{
switch (type)
{
case unionType::f: upFloat.~unique_ptr<float>(); break;
case unionType::i: upInt.~unique_ptr<int>(); break;
}
}
};
int main()
{
std::vector<std::unique_ptr<myStruct>> structVec;
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(3.14f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(739)));
structVec.push_back(std::make_unique<myStruct>());
structVec.push_back(std::make_unique<myStruct>(std::make_unique<float>(8.95f)));
structVec.push_back(std::make_unique<myStruct>(std::make_unique<int>(3)));
structVec.push_back(std::make_unique<myStruct>());
for(auto &a: structVec)
{
if(a->type == myStruct::unionType::none)
{
std::cout << "Struct Has Unallocated Union" << std::endl;
}
else if(a->type == myStruct::unionType::f)
{
std::cout << "Struct float = " << *a->upFloat << std::endl;
}
else
{
std::cout << "Struct int = " << *a->upInt << std::endl;
}
std::cout << std::endl;
}
return 0;
}
Outputs:
Struct float = 3.14
Struct int = 739
Struct Has Unallocated Union
Struct float = 8.95
Struct int = 3
Struct Has Unallocated Union

Changing the active member of a union requires special care to object lifetime. The C++ Standard says (9.5p4):
Note: In general, one must use explicit destructor calls and placement new operators to change the active
member of a union.
When the members are plain old data, it generally "just works", even though you aren't calling constructors (using placement new) and destructors. That's because the lifetime for objects with trivial initialization begins "when storage is obtained" of sufficient size and correct alignment, and the union provides that.
Now you've got members with non-trivial constructor and destructor. Their lifetime doesn't begin when storage is obtained, you also have to cause initialization to finish. And that means placement new. Skipping destructor calls isn't safe either, you get undefined behavior if those destructors would have had side effects your program relies on (and a unique_ptr destructor has the side effect of deallocating its target).
Thus you are calling a move-assignment operator on a member whose lifetime hasn't begun. That is undefined behavior.

For unrestricted union, you have to manage yourself some construct/destruction.
Following may help:
union myUnion{
std::unique_ptr<float> upFloat;
std::unique_ptr<int> upInt;
myUnion(){ new (&upFloat) std::unique_ptr<float>{};}
~myUnion() {}
};
class myStruct
{
public:
~myStruct()
{
destroy();
}
void destroy()
{
switch (type)
{
case unionType::f: num.upFloat.~unique_ptr<float>(); break;
case unionType::i: num.upInt.~unique_ptr<int>(); break;
}
}
void set(std::unique_ptr<int> p)
{
destroy();
new (&num.upInt) std::unique_ptr<int>{std::move(p)};
type = unionType::i;
}
void set(std::unique_ptr<float> p)
{
destroy();
new (&num.upFloat) std::unique_ptr<float>{std::move(p)};
type = unionType::f;
}
public:
enum class unionType {f, i} type = unionType::f; // match the default constructor of enum
myUnion num;
};
int main()
{
myStruct aStruct, bStruct;
aStruct.set(std::make_unique<float>(3.14f));
bStruct.set(std::make_unique<int>(3));
std::cout << "aStruct float = " << *aStruct.num.upFloat << std::endl;
std::cout << "bStruct int = " << *bStruct.num.upInt << std::endl;
return 0;
}
In C++17, you may use std::variant instead of your own struct

From this reference:
If a union contains a non-static data member with a non-trivial special member function (copy/move constructor, copy/move assignment, or destructor) that function is deleted by default in the union and needs to be defined explicitly by the programmer.
I assume that the reason you wrapped the pointers in simple structures is because you could not build it otherwise, due to the restrictions imposed by the above paragraph.
What you have done instead is bypassed the compilers safety-guards, and probably have undefined behavior in your code.

From ยง12.6.2[class.base.init]/p8 of the standard (emphasis added):
In a non-delegating constructor, if a given non-static data member or
base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
otherwise, if the entity is a variant member (9.5), no initialization is performed;
[...]
Union members are variant members, which means that the unique_ptrs are left uninitialized. In particular, no constructor, not even the default one, is called. Technically, the lifetime of these unique_ptrs never even began.
The unique_ptr move assignment operator must delete what the unique_ptr is currently holding, but you are move-assigning to an uninitialized "unique_ptr" containing garbage values. As a result, your move assignment likely caused an attempt to delete a garbage pointer, causing a segfault.

Related

How come new object's members are not uninitialized with std::make_unique?

Despite the default values of X, Y and Z in Position, when it is initialized in create() none of these variables seem to have been initialized at all. I can set them and then retrieve them, but the default value is never seen. I've tried initializing them various ways but with no success.
How do I use std::make_unique to return a unique_ptr of type T with its default values set?
#include <iostream>
#include <unordered_map>
#include <memory>
#include <typeindex>
class Component
{
public:
Component() {};
virtual ~Component() {};
};
class Position : public Component
{
public:
Position(){};
float x = 123;
float y = 321;
float z = 666;
};
std::unordered_map<std::type_index, std::unordered_map<uint32_t, std::unique_ptr<Component>>> components;
template<typename T>
T& get(uint32_t id)
{
return reinterpret_cast<T&>(components[std::type_index(typeid(T))].at(id));
}
template<typename T>
void create(uint32_t id)
{
components[std::type_index(typeid(T))].emplace(id, std::make_unique<T>());
}
int main()
{
create<Position>(8);
std::cout << get<Position>(8).z << std::endl; //Value not initialized
get<Position>(8).z;
std::cout << get<Position>(8).z << std::endl; //Still not
get<Position>(8) = Position();
std::cout << get<Position>(8).z << std::endl; //Now works, but unwanted creation of temporary Position
get<Position>(8).z = 42;
std::cout << get<Position>(8).z << std::endl; //Works
//New try
create<Position>(8);
return 0;
}
The problem is in your get method. Change it as below and it should resolve the issue.
return reinterpret_cast<T&>(*(components[std::type_index(typeid(T))].at(id)));
Your components[std::type_index(typeid(T))] returns another map and the .at() returns a std::unique_ptr. You were originally casting the unique_ptr using reinterpret_cast which resulted in undefined behavior.
While we are on the subject, do not use reinterpret_cast for casting across hierarchies. Use dynamic_cast. The dynamic_cast has a well defined behavior when the casting fails for both references and pointers.
In short, you were doing a reinterpret_cast<Position&>(uniquePtrToPosition) which is illegal C++.

Some confusions about pointers to data-member in C++

class Foo {
public:
int a = 1;
int b = 2;
};
int main() {
Foo foo;
cout << &Foo::a << endl;//output 1
cout << &Foo::b << endl;//also output 1
}
As we know pointers to member data should point out the relative offset from the start address of the object, but as the example shows, both pointers to Foo::a and Foo::b get a 1. Would anyone can explain what happened here?
First of all, a pointer-to-member is NOT required to be implemented as an offset from the "start address of the object" (a concept which is not part of the language standard). And indeed, certain types of member pointers couldn't be implemented like that.
What you're seeing, instead, is simply basic_ostream::operator<<(bool). Pointers to members can't be implicitly converted to many other types, but they can be converted to bool. The 1 you're seeing is simply an indication that you've passed a non-null member pointer.
Although the answer provided by Sneftel is correct, this is one way to view the "actual" (internal) value of the pointer-to-members:
#include <iostream>
struct x {
int a, b;
};
int main() {
int x::* pa = &x::a;
int x::* pb = &x::b;
std::cout << pa << ' ' << pb << '\n';
std::cout << *(int*)&pa << ' ' << *(int*)&pb << '\n';
}
Try it online!
This may have different values or cause undefined behavior, depends on the implementation. Also there is no guarantee that sizeof int == sizeof pa.
Most compilers support use of the offsetof macro defined in <cstddef>. Just beware the pitfalls, quoting cppreference :
If type is not a standard layout type, the behavior is undefined
(until C++17)use of the offsetof macro is conditionally-supported
(since C++17).
If member is a static member or a member function, the behavior is
undefined.
Example:
#include <iostream>
#include <cstddef>
class Foo {
public:
int a;
int b;
};
int main() {
std::cout << offsetof(Foo, a) << std::endl;
std::cout << offsetof(Foo, b) << std::endl;
}

Would this restrict the class to be have a lifetime in the current frame only?

I wanted to restrict a specific class to be creatable on the stack only (not via allocation). The reason for this is that on the stack, the object which lifetime has begun last, will be the first to be destroyed, and I can create a hierarchy. I did it like this:
#include <cstddef>
#include <iostream>
class Foo {
public:
static Foo createOnStack() {
return {};
}
~Foo () {
std::cout << "Destructed " << --i << std::endl;
}
protected:
static int i;
Foo () {
std::cout << "Created " << i++ << std::endl;
}
Foo (const Foo &) = delete;
};
int Foo::i = 0;
The constructor normally should push the hierarchy stack, and the destructor pops it. I replaced it here for proof of concept. Now, the only way you can use such an object is by storing it in a temporary reference like this:
int main() {
Foo && a = Foo::createOnStack();
const Foo& b = Foo::createOnStack();
return 0;
}
My question now is, how safe is this with the C++ standard? Is there still a way to legally create a Foo on the heap or hand it down from your function into another frame (aka return it from your function) without running into undefined behaviour?
EDIT: link to example https://ideone.com/M0I1NI
Leaving aside the protected backdoor, C++17 copy elision breaks this in two ways:
#include<iostream>
#include<memory>
struct S {
static S make() {return {};}
S(const S&)=delete;
~S() {std::cout << '-' << this << std::endl;}
private:
S() {std::cout << '+' << this << std::endl;}
};
S reorder() {
S &&local=S::make();
return S::make();
}
int main() {
auto p=new S(S::make()),q=new S(S::make()); // #1
delete p; delete q;
reorder(); // #2
}
The use of new is obvious and has been discussed.
C++17 also allows prvalues to propagate through stack frames, which means that a local can get created before a return value and get destroyed while that return value is alive.
Note that the second case already existed (formally in C++14 and informally long before) in the case where local is of type S but the return value is some other (movable) type. You can't assume in general that even automatic object lifetimes nest properly.

Default initialization

#include <iostream>
using namespace std;
class Apple
{
public:
int i;
string s = "HelloWorld";
string s1;
bool b;
};
int main(int argc, char *argv[]) {
Apple a; //doesn't this trigger default initialization??
cout << a.i << a.s << a.s1 << a.b;
}
If the object is a local variable, then the data members will be be default-initialised.
But here is the output: 0HelloWorld0. Isn't this value initialization??
Here's a simple test that indicates no value initialization is being performed in your code.
#include <iostream>
#include <string>
using namespace std;
class Apple
{
public:
int i;
string s = "HelloWorld";
string s1;
bool b;
};
int main()
{
{
Apple a;
a.i = 42;
a.b = true;
cout << a.i << a.s << a.s1 << a.b;
}
{
Apple a;
cout << a.i << a.s << a.s1 << a.b;
}
{
Apple a{};
cout << a.i << a.s << a.s1 << a.b;
}
}
Output:
42HelloWorld142HelloWorld10HelloWorld0
As you can see, in the second case those members still contain values from the previous assignment. Note that I'm not saying this is guaranteed or standard defined behavior.
In the third case you are indicating that the object should be value-initialized by adding the braces.
If your compiler doesn't support C++11's uniform initialization syntax, you can change it to the following to achieve the same effect.
Apple a = Apple();
Isn't this value initialization??
On my machine, your program outputs -1219700747HelloWorld244, a clear indicator for default initialisation.
That you get 0HelloWorld0 isn't fully random and can have many reasons. For example, it has something to do with your os architecture. All new memory for each program is initially zeroed, and this is why in this simple example, everything is set to zero. Or the compiler initializes the struct statically for performance reasons.
To quote the standard at 12.6.2/8:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a
mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no
ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
โ€” if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized
as specified in 8.5;
โ€” otherwise, if the entity is an anonymous union or a variant member (9.5), no initialization is performed;
โ€” otherwise, the entity is default-initialized (8.5).

Does C++ do value initialization of a POD typedef?

Does C++ do value initialization on simple POD typedefs?
Assuming
typedef T* Ptr;
does
Ptr()
do value-initialization and guarantee to equal (T*)0?
e.g.
Ptr p = Ptr();
return Ptr();
It does. For a type T, T() value-initializes an "object" of type T and yields an rvalue expression.
int a = int();
assert(a == 0);
Same for pod-classes:
struct A { int a; };
assert(A().a == 0);
Also true for some non-POD classes that have no user declared constructor:
struct A { ~A() { } int a; };
assert(A().a == 0);
Since you cannot do A a() (creates a function declaration instead), boost has a class value_initialized, allowing to work around that, and C++1x will have the following, alternative, syntax
int a{};
In the dry words of the Standard, this sounds like
The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized
Since a typedef-name is a type-name, which is a simple-type-specifier itself, this works just fine.
#include <iostream>
struct Foo {
char bar;
char baz;
char foobar;
// the struct is a POD
//virtual void a() { bar='b'; }
};
int main() {
Foo o1;
Foo o2 = Foo();
std::cout << "O1: " << (int)o1.bar <<" "<< (int)o1.baz <<" "<< (int)o1.foobar << std::endl;
std::cout << "O2: " << (int)o2.bar <<" "<< (int)o2.baz <<" "<< (int)o2.foobar << std::endl;
return 0;
}
Output:
O1: -27 -98 0
O2: 0 0 0
Adding () propagates initializer calls to all POD members. Uncomenting the virtual method changes output to:
O1: -44 -27 -98
O2: -71 -120 4
However adding destructor ~Foo() does not suppress the initialization, although it creates non-POD object (output similar to first one).