Is there a better way to build a move constructor for a union-like class? If I were to have a union-like class like the class in the following code, is there a way to build the class or the move constructor that wouldn't require switch statement like the move constructor in the following code.
class S {
private:
enum {CHAR, INT, DOUBLE} type; // tag
// anonymous union
union {
char c;
int n;
double d;
};
public:
// constructor if the union were to hold a character
AS(const char c) {
this->tag = AS::CHAR;
this->c = c;
}
// constructor if the union were to hold a int
AS(const int i) {
this->tag = AS::INT;
this->n = i;
}
// constructor if the union were to hold a double
AS(const double d) {
this->tag = AS::DOUBLE;
this->d = d;
}
// Move constructor with switch statement
S(S &&src) : type(std::move(src.type)) {
switch(type) {
case CHAR:
this->c = src.c);
src.c = 0;
break;
case INT:
this->n = src.n;
src.n = 0;
break;
case DOUBLE:
this->d = src.d;
src.d = 0
break;
default:
break;
}
}
};
No, there is no better way. If you want to safely move from a union containing arbitrary types, you must do so from the field of the union that has last been written to (if any). The other answer stating the contrary is wrong, consider an example like
union SomethingLikeThisIsGoingToHappenInPractice {
std::string what_we_actually_want_to_move;
char what_we_do_not_care_about[sizeof(std::string)+1];
};
If you use the move constructor of the 'largest' type here you'd have to pick the char array here, despite moving that actually not really doing anything. If the std::string field is set you'd hope to move its internal buffer, which is not going to happen if you look at the char array. Also keep in mind that move semantics is about semantics, not about moving memory. If that was the issue you could just always use memmove and be done with it, no C++11 required.
This isn't even going into the problem of reading from a union member you haven't written to being UB in C++, even for primitive types but especially for class types.
TL;DR if you find yourself in this situation use the solution originally proposed by the OP, not what's in the accepted answer.
PS: Of course if you are just moving a union that only contains things that are trivially moveable, like primitive types, you could just use the default move constructor for unions which does just copy the memory; In that case it's really not worth it to have a move constructor in the first place though, other than for consistencies sake.
Since unions as a data type refer to the same place in memory for all of the fields inside (although the ordering of smaller fields like the 4 bytes of char array[4] within that space depends on the system), it is possible to just move the union using the largest field. This will ensure that you move the entire union every time, regardless of which fields you are currently using the union for.
class S {
private:
enum {CHAR, INT, DOUBLE} type; // tag
// anonymous union
union {
char c;
int n;
double d;
};
public:
// Move constructor with switch statement
S(S &&src) : type(std::move(src.type)) {
this->d = src.d;
src.d = 0;
}
};
Yes, there is better way. At first it have to add EMPTY tag, after this use delegating copy constructor:
class S {
private:
enum {EMPTY, CHAR, INT, DOUBLE} type; // tag
// anonymous union
union {
char c;
int n;
double d;
};
public:
S(){ this->type = EMPTY; }
// constructor if the union were to hold a character
S(const char c) {
this->type = CHAR;
this->c = c;
}
// constructor if the union were to hold a int
S(const int i) {
this->type = INT;
this->n = i;
}
// constructor if the union were to hold a double
S(const double d) {
this->type = DOUBLE;
this->d = d;
}
S(const S& src) = default;// default copy constructor
// Move constructor
S(S&& src)
: S(src) // copy here
{
src.type = EMPTY; // mark src as empty
}
};
Of course, this example is not very useful, unlike the example below, which adds work with a pointer to a string:
#include <cassert>
#include <iostream>
#include <memory>
#include <string>
class S {
public:
enum Tag {EMPTY, CHAR, INT, DOUBLE, STRING};
private:
Tag type;
// anonymous union
union {
char c;
int n;
double d;
std::string* str;
};
public:
S(){ this->type = EMPTY; }
// constructor if the union were to hold a character
S(const char c) {
this->type = CHAR;
this->c = c;
}
// constructor if the union were to hold a int
S(const int i) {
this->type = INT;
this->n = i;
}
// constructor if the union were to hold a double
S(const double d) {
this->type = DOUBLE;
this->d = d;
}
S(std::unique_ptr<std::string> ptr) {
this->type = STRING;
this->str = ptr.release();
}
std::unique_ptr<std::string> ExtractStr()
{
if ( this->type != STRING )
return nullptr;
std::string* ptr = this->str;
this->str = nullptr;
this->type = EMPTY;
return std::unique_ptr<std::string>{ptr};
}
Tag GetType() const
{
return type;
}
private:
// only move is allowed for public
S(const S& src) = default;// default copy constructor
S& operator = (const S& src) = default;// default copy assignment operator
public:
// Move constructor
S(S&& src)
: S(src) // copy here (but we copy only pointer for STRING)
{
src.type = EMPTY; // mark src as empty
}
S& operator = (S&& src)
{
if ( this->type == STRING )
ExtractStr();// this call frees memory
this->operator=(src);
src.type = EMPTY;
return *this;
}
~S()
{
if ( this->type == STRING )
{
ExtractStr();// this call frees memory
}
}
};
// some test
int main()
{
S sN(1);
S sF(2.2);
S x{std::move(sN)};
std::unique_ptr<std::string> pStr1 = std::make_unique<std::string>("specially for Daniel Robertson");
std::unique_ptr<std::string> pStr2 = std::make_unique<std::string>("example ");
std::unique_ptr<std::string> pStr3 = std::make_unique<std::string>("~S() test");
S xStr1(std::move(pStr1));
S xStr2(std::move(pStr2));
S xStr3(std::move(pStr3));
x = std::move(xStr1);
assert(xStr1.GetType() == S::EMPTY);
assert(x.GetType() == S::STRING);
std::string str2 = *xStr2.ExtractStr();
std::cout << str2 << *x.ExtractStr() << std::endl;
return 0;
}
Related
So I understand that you can't have pointers to bit-fields because the pointers can only distinguish addresses to the byte level, not bit level. References to bit-fields are also not allowed. Are there any other ways that I would be able to reference the members of the bit field indirectly? Ideally I would be able to access them following using array syntax similar to the mys1array line below. I know arrays of references are illegal but perhaps someone has some sage knowledge out there about some other mechanisms which could achieve a similar goal.
typedef struct{
unsigned short a : 5;
unsigned short b : 3;
unsigned short c : 8;
}myStruct;
class myClass{
public:
myStruct s1;
//unsigned short &mys1array[] = {&s1.a, &s1.b ,&s1.c};
};
You could use an array of function pointers that are initialized by lambdas to access each element of the bitfield with the different functions.
class myClass {
public:
myStruct s1;
static constexpr unsigned short (*accessors)(myStruct const &s)[] = {
+[](myStruct const &s) -> unsigned short { return s.a; }
// ...
};
};
With this you have to pass an instance of myStruct to the functions. Another method is using std::function and use capturing lambdas:
class myClass {
public:
myStruct s1;
std::function<unsigned short()> accessors[3];
myClass(myStruct s)
: s1(s),
accessors{
[this]() -> unsigned short { return this->s1.a; },
// ...
}
{}
// ...
};
Don't forget that with this, you have to implement copy and move constructors and assignment operators, as the lambda captures this.
You can only access bit fields through the class. You can get indirection by having a pointer or reference to the enclosing class object.
You could write a custom iterator if you wanted to iterate the bitfields within the class, but implementation of such iterator may require some explicit hard-coding since C++ lacks reflection capabilities to automate it. Here is an incomplete proof-of-concept:
struct myStruct {
unsigned short a : 5;
unsigned short b : 3;
unsigned short c : 8;
struct reference {
myStruct* parent;
unsigned char field;
operator unsigned short() {
switch(field) {
case 0: return parent->a;
case 1: return parent->b;
case 2: return parent->c;
default: assert(false);
}
}
reference& operator=(unsigned short u) {
switch(field) {
case 0: parent->a = u; return *this;
case 1: parent->b = u; return *this;
case 2: parent->c = u; return *this;
default: assert(false);
}
}
void operator++() {
++field;
}
friend auto operator<=>(const reference&, const reference&) = default;
};
struct iterator
{
//TODO add missing member definitions, const overloads etc.
reference current;
reference operator*() {
return current;
}
void operator++() {
++current;
}
friend auto operator<=>(const myStructIterator&, const myStructIterator&) = default;
};
iterator begin() {
return {this, 0};
}
iterator end() {
return {this, 3};
}
};
int main()
{
myStruct s {};
for(int i=3; auto f : s) {
f = i++;
}
for(auto f : s) {
std::cout << f << '\n';
}
}
The reference class is sufficient to represent indirection for the bit fields and the iterator allows treating the fields as an iterable range.
I'm working with a union which has a member that is a class that uses diamond inheritance, but the program encounters a segmentation fault upon assignment to this member.
My suspicion is that I need to add some copy constructors, but after multiple attempts, the correct way to do this still evades me.
I've included a minimal, reproducible example here.
struct Base
{
Base() : a(0) {}
Base(int x) : a(x) {}
int a;
};
struct Derived1 : virtual public Base
{
Derived1() {}
};
struct Derived2 : virtual public Base
{
Derived2() {}
};
struct Final : public Derived1, public Derived2
{
Final() {}
};
union Example
{
Final value;
int i;
};
int main()
{
Example example{ Final() };
example.i = -1;
/* Segfault on the line below.
* If the above line
example.i = -1;
* is removed, the segfault is not encountered. */
example.value = Final();
}
My thanks to anyone who knows how to do this.
Since the Base class is virtual, most likely there are some internal control structures, which become destroyed, when you assign
example.i = -1;
When you recreate value, e.g.
new(&example.value) Final();
example.value = Final();
before the assignment, the segmentation fault goes away. Although, I wouldn't recommend this hack.
As already mentioned in the comments, a std::variant would be more appropriate, if you have C++17 available. The example would then become
std::variant<Final, int> example{ Final() };
example = -1;
example = Final();
Begin with c++11, unions with members that define their own constructors
and/or copy-control members are allowed but When a union has members of built-in type, we can use ordinary assignment to
change the value that the union holds, but Not for unions that have members of
nontrivial class types. When we switch the union’s value to and from a member of
class type, we must construct or destroy that member. for example see bellow code
#include <iostream>
#include <new> // for placement new
class Type // non built in type with constructors
{
private:
int val;
public:
Type() : val{0} { }
explicit Type(int v) : val{v} { }
Type(const Type &obj) : val{obj.val} { }
int getval() { return val; }
};
class unionexample // I enclose union with class for readability
{
private:
enum { INT,TYPE } OBJ;
union
{
Type Tval; // non build in type
int ival; // build in type
};
public:
unionexample() : ival{0}, OBJ{INT} // default with int
{ }
unionexample(const unionexample &obj) : OBJ{obj.OBJ}
{
switch (obj.OBJ)
{
case INT:
this->ival = obj.ival;
break;
case TYPE:
new (&this->Tval) Type(obj.Tval);
break;
}
}
unionexample &operator=(int v)
{
if (OBJ == TYPE)
{
Tval.~Type(); // if it is TYPE destruct it
}
ival = v; // assign
OBJ = INT;
return *this;
}
unionexample &operator=(Type v)
{
if (OBJ == TYPE)
{
Tval = v; // if it is alderdy Type just assign
}
new (&Tval) Type(v); // else construct
OBJ = TYPE;
return *this;
}
void print()
{
switch (OBJ)
{
case INT:
std::cout << "ival = " << ival << std::endl;
break;
case TYPE:
std::cout << "Tval = " << Tval.getval() << std::endl;
break;
}
}
~unionexample()
{
if (OBJ == TYPE) // if it is TYPE we must destruct it
Tval.~Type();
}
};
int main()
{
unionexample ue;
ue.print();
ue = Type(1);
ue.print();
}
output:
ival = 0
Tval = 1
Run Here
In Your case
int main()
{
Example example{ value : Final() }; // construct with Final
example.value.~Final(); // destruct Final
example.i = -1; // assign int built in type
new(&example.value) Final(); // construct
example.value.~Final(); // destruct finally
}
If you asked this question other than learning purpose you can use std::variant as in other answer.
Thanks.
I want to store and retrieve different data types in one object. This will late be an interface to a database.
Basic types (char, short, int, float, double, ...) should go into a union to save storage space. In addition, I want to store std::string and std::vector. Storage is done by overloading the assignment operator, retrieval is done by conversion operator overloading. The code below is a stripped-down version working with int, double and std::string.
While the int and double parts work fine, the std::string part fails. I'm also not sure if the operator overloading is the most elegant way to get the data in and out. I'm restricted to C++11, so std::any is not an option.
#include <string>
class mydata {
private:
enum {
TID_INT,
TID_DOUBLE,
TID_STRING
};
int m_type_id; // stores one of TID_xxx
// this object should be able to store int, double, std::string
union {
int m_int;
double m_double;
} m;
std::string m_string;
public:
// Default constructor
mydata() { m_type_id = 0; }
// Overloading the Assignment Operator
template <typename T>
const T &operator=(const T &v) {
if (typeid(v) == typeid(int)) {
m_type_id = TID_INT;
m.m_int = v;
} else if (typeid(v) == typeid(double)) {
m_type_id = TID_DOUBLE;
m.m_double = v;
} else if (typeid(v) == typeid(const char *)) {
m_type_id = TID_STRING;
// this fails -->
//m_string = std::string(v);
}
return v;
}
operator int() {
if (m_type_id == TID_INT)
return m.m_int;
else if (m_type_id == TID_DOUBLE)
return (int) m.m_double;
return 0; // maybe throw an exception here
}
operator double() {
if (m_type_id == TID_INT)
return (double)m.m_int;
else if (m_type_id == TID_DOUBLE)
return m.m_double;
return 0;
}
};
/*------------------------------------------------------------------*/
int main() {
mydata d1, d2, d3;
d1 = 123; // store an int
d2 = 456.789; // store a double
int i = d1;
i = d2;
double d = d1;
d = d2;
// this fails --->
// d3 = "Hello"; // store a string
return 1;
}
Finally figured it out. I have to split the assignment operator between different types.
Also need to overload the conversion operators to retrieve the values in different types.
Here is the code:
#include <string>
class mydata {
private:
enum {
TID_INT,
TID_DOUBLE,
TID_STRING
};
int m_type_id; // stored one of TID_xxx
// this object stored int, double, std::string
union {
int m_int;
double m_double;
};
std::string m_string;
public:
// Default constructor
mydata() : m_type_id(0), m_int(0) {}
// Overloading the Assignment Operator
const int &operator=(const int &v) {
m_type_id = TID_INT;
m_int = v;
return v;
}
const double &operator=(const double &v) {
m_type_id = TID_DOUBLE;
m_double = v;
return v;
}
const std::string &operator=(const std::string &v) {
m_type_id = TID_STRING;
m_string = v;
return v;
}
// overloading the conversion operators
operator std::string() {
return m_string;
}
template <typename T>
operator T() {
if (m_type_id == TID_INT)
return (T)m_int;
else if (m_type_id == TID_DOUBLE)
return (T)m_double;
return 0;
}
};
/*------------------------------------------------------------------*/
int main() {
mydata d1, d2, d3;
d1 = 123; // store an int
d2 = 456.789; // store a double
int i = d1;
i = d2;
double d = d1;
d = d2;
d3 = "Hello";
std::string s = d3;
return 1;
}
In a copy constructor of a struct/class, how can I avoid copying all the basic (int, double, etc.) members one by one if the intention is to copy a pointer successfully? Is it possible to extend the default copy constructor in this sense?
struct Type
{
int a;
double b;
bool c;
// ... a lot of basic members
int* p;
Type()
{
p = new int;
*p = 0;
}
Type (const Type& t)
{
// how to avoid copying these members one by one
this.a = t.a;
this.b = t.b;
this.c = t.c;
// but only add this portion
this.p = new int;
*this.p = *t.p;
}
};
Create an RAII wrapper for the int * data member that allows copying/moving.
struct DynInt
{
std::unique_ptr<int> p;
DynInt() : DynInt(0) {}
explicit DynInt(int i) : p(new int(i)) {}
DynInt(DynInt const &other) : p(new int(*other.p)) {}
DynInt& operator=(DynInt const& other)
{
*p = *other.p;
return *this;
}
DynInt(DynInt&&) = default;
DynInt& operator=(DynInt&&) = default;
// maybe define operator* to allow direct access to *p
};
Then declare your class as
struct Type
{
int a;
double b;
bool c;
// ... a lot of basic members
DynInt p;
};
Now, the implicitly generated copy constructor will do the right thing.
I am having trouble with the initialization of this struct (simplified for example)
struct S{ const float * const * const data;};
Basically I have a buffer of buffers of floats, and I use const to ensure someone using S cannot change anything to this member (read only).
My problem is that this is complicated and hard to read to initialize, I would like to use a lambda that return an const S, and so I could initialize members in my lambda by writing the member name : s.data = ptr;
Now this code is complex and I wonder what could be a better solution.
AFAIK, having struct S{float ** data;} a const S would not protect as efficiently the content of the member, I could not modify S::data, but I could modify *S::data.
How should I do ?
Thank you
Why not just remove the last const?
struct S{ const float * const * data;};
That way you can initialize data however you like, and it still can't be used to modify anything it points to.
data itself can be modified, but should that be prevented, it should simply be private.
The recommended way is to add a constructor to S. This allows you to set the value of data in the ctor initializer list.
struct S
{
explicit S(const float *const *const d) : data(d) {}
const float * const * const data;
};
S GetS()
{
float **data = GetData();
return S(data);
}
If you want to restrict who can change S::data after it has been initialized you can box the member variable and use friendship to allow access. This requires encapsulating the data member in an additional struct that provides conversion and assignment operators.
struct Outer
{
struct S
{
private:
struct ConstBox
{
friend Outer;
ConstBox(const ConstBox& other) : data_(other.data_) {}
explicit ConstBox(const float *const *const data) : data_(data) {}
operator const float* const* () const { return data_; }
private:
ConstBox& operator=(const float * const * data)
{
data_ = data;
return *this;
}
const float * const * data_;
};
public:
S() : data(nullptr) {}
explicit S(const float *const *const d) : data(d) {}
ConstBox data;
};
S DoSomething() const
{
S s(nullptr);
auto f = []() -> S
{
S s;
s.data = new float*[10];
return s;
};
return f();
}
};
typedef Outer::S S;
void FailTest()
{
S s;
s.data = nullptr; // <-- fails
float** v1 = s.data; // <-- fails
const float** v1 = s.data; // <-- fails
// These are ok
const float* const* v2 = s.data;
}
#CaptainObvious's answer is the correct one. Write a constructor for S, taking whatever arguments it needs, and use a member initialiser rather than an assignment statement to set 'data'.
With your simplified example I would simply do:
struct S{ float const * const * const data;};
auto create_buffer() -> float const * const * {
float **buf;
/* ... compute buffer contents */
return buf;
}
S s {create_buffer()};
However, in a comment you mention that you have many members and that initializing members based on order is not sufficiently clear.
struct S { const A a; const B b; const C c; };
S s {x,y,z}; // order based, not readable enough.
const members must be initialized as part of the object's construction. You must either specify them somehow in the initializer, or you must set their value in the class, so that they are set at construction time.
Solution 1
One way to pass them during construction, but in a readable fashion is to use a second object to help initialization:
struct S_initializer { A a; B b; C c; }
struct S {
const A a; const B b; const C c;
S(S_initializer &s) : a(s.a), b(s.b), c(s.c) {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The above involves some repitition, which you can avoid by just making the initialization helper object a const member of S:
struct S {
const S_initializer m;
S(S_initializer &s) : m{s} {}
};
S make_S() {
S_initializer s;
s.a = x;
s.b = y;
s.c = z;
return S{s};
}
The tradeoff is that now to access the members of S you have to have an extra .m in there:
A a = s.m.a; // versus just s.a;
Solution 2
A second method relies on a compiler extension; Although not standard C++, gcc and clang implement C99 designated initializers in C++. VC++ does not implement this.
S s { .a = x, .b = y, .c = z };