I'm trying to use an union (C++) that has some non-primitive variables, but I'm stuck trying to create the destructor for that class. As I have read, it is not possible to guess what variable of the union is being used so there is no implicit destructor, and as I'm using this union on the stack, the compiler errors that the destructor is deleted. The union is as follows:
struct LuaVariant {
LuaVariant() : type(VARIANT_NONE) { }
LuaVariantType_t type;
union {
std::string text;
Position pos;
uint32_t number;
};
};
The type variable holds what field of the union is being used (chosen from an enum), for the purpose of reading from the union and it could be used to guess what value should be deleted. I tried some different approaches but none of them worked. First of all, just tried the default destructor:
~LuaVariant() = default;
It did not work, as the default is... deleted. So, I tried swapping the value with an empty one, so that the contents would be erased and there would be no problem "leaking" an empty value:
~LuaVariant() {
switch (type) {
case VARIANT_POSITION:
case VARIANT_TARGETPOSITION: {
Position p;
std::swap(p, pos);
break;
}
case VARIANT_STRING: {
std::string s;
std::swap(s, text);
break;
}
default:
number = 0;
break;
}
};
But as I am not a master of the unions, I don't know if that can cause other problems, such as allocated memory that never gets deallocated, or something like that. Can this swap strategy be used without flaws and issues?
This grouping (union + enum value for discriminating type) is called a discriminated union.
It will be up to you to call any construction/destruction, because the union itself cannot (if it could, it would also be able to discriminate for initialized/non-initialized types within the union, and you would not need the enum).
Code:
class LuaVariant // no public access to the raw union
{
public:
LuaVariant() : type(VARIANT_NONE) { }
~LuaVariant() { destroy_value(); }
void text(std::string value) // here's a setter example
{
using std::string;
destroy_value();
type = VARIANT_TEXT;
new (&value.text) string{ std::move(value) };
}
private:
void destroy_value()
{
using std::string;
switch(type)
{
case VARIANT_TEXT:
(&value.text)->string::~string();
break;
case VARIANT_POSITION:
(&value.pos)->Position::~Position();
break;
case VARIANT_NUMBER:
value.number = 0;
break;
default:
break;
}
}
LuaVariantType_t type;
union {
std::string text;
Position pos;
uint32_t number;
} value;
};
If you want to use std::string in a union in C++11, you have to explicitly call its destructor, and placement new to construct it. Example from cppreference.com:
#include <iostream>
#include <string>
#include <vector>
union S {
std::string str;
std::vector<int> vec;
~S() {} // needs to know which member is active, only possible in union-like class
}; // the whole union occupies max(sizeof(string), sizeof(vector<int>))
int main()
{
S s = {"Hello, world"};
// at this point, reading from s.vec is UB
std::cout << "s.str = " << s.str << '\n';
s.str.~basic_string<char>();
new (&s.vec) std::vector<int>;
// now, s.vec is the active member of the union
s.vec.push_back(10);
std::cout << s.vec.size() << '\n';
s.vec.~vector<int>();
}
Related
I come from C/C# language and now I'm trying to learn about C++ and his standards functions.
Now, I'm creating a class called IMonsterDead. I will have a std::vector<IMonsterDead*> with N monsters.
Example:
class IMonsterDead {
public:
IMonsterDead(int Id)
{
this->_Id = Id;
}
virtual void OnDead() = 0;
int Id() const {
return _Id;
}
private:
int _Id;
};
One class which implements that class:
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
virtual void OnDead()
{
std::cout << "MonsterTesd died" << std::endl;
}
};
Ok, if I access directly everything works fine. But I'm trying to use std::find.
Full program test:
int main()
{
std::vector<IMonsterDead*> monsters;
for (int i = 0; i < 1000; i++)
{
monsters.emplace_back(new MonsterTest(1000 + i));
}
int id = 1033;
std::vector<IMonsterDead*>::iterator result = std::find(monsters.begin(), monsters.end(), [id]( IMonsterDead const* l) {
return l->Id() == id;
});
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
// Here I want to access OnDead function from result
}
return 0;
}
So I need to access OnDead function from result but I can't. Intellisense doesn't show anything for me. The result exists.
How can I access that function? Have another better way to do that?
You need to use std::find_if() instead of std::find(). std::find() is for finding an element with a specific value, so you have to pass it the actual value to find, not a user_defined predicate. std::find_if() is for finding an element based on a predicate.
Either way, if a match is found, dereferencing the returned iterator will give you a IMonsterDead* pointer (more accurately, it will give you a IMonsterDead*& reference-to-pointer). You need to then dereference that pointer in order to access any members, like OnDead().
You are also leaking memory. You are not delete'ing the objects you new. And when dealing with polymorphic types that get deleted via a pointer to a base class, the base class needs a virtual destructor to ensure all derived destructors get called properly.
With that said, you are clearly using C++11 or later (by the fact that you are using vector::emplace_back()), so you should use C++11 features to help you manage your code better:
You should use std::unique_ptr to wrap your monster objects so you don't need to delete them manually.
You should always use the override keyword when overriding a virtual method, to ensure you override it properly. The compiler can catch more syntax errors when using override than without it.
You should use auto whenever you declare a variable that the compiler can deduce its type for you. Especially useful when dealing with templated code.
Try something more like this:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class IMonsterDead {
public:
IMonsterDead(int Id)
: m_Id(Id)
{
}
virtual ~IMonsterDead() {}
virtual void OnDead() = 0;
int Id() const {
return m_Id;
}
private:
int m_Id;
};
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
void OnDead() override
{
std::cout << "MonsterTest died" << std::endl;
}
};
int main()
{
std::vector<std::unique_ptr<IMonsterDead>> monsters;
for (int i = 0; i < 1000; i++)
{
// using emplace_back() with a raw pointer risks leaking memory
// if the emplacement fails, so push a fully-constructed
// std::unique_ptr instead, to maintain ownership at all times...
monsters.push_back(std::unique_ptr<IMonsterDead>(new MonsterTest(1000 + i)));
// or:
// std::unique_ptr<IMonsterDead> monster(new MonsterTest(1000 + i));
// monsters.push_back(std::move(monster));
// or, if you are using C++14 or later:
// monsters.push_back(std::make_unique<MonsterTest>(1000 + i));
}
int id = 1033;
auto result = std::find_if(monsters.begin(), monsters.end(),
[id](decltype(monsters)::value_type &l) // or: (decltype(*monsters.begin()) l)
{
return (l->Id() == id);
}
// or, if you are using C++14 or later:
// [id](auto &l) { return (l->Id() == id); }
);
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
auto &monster = *result; // monster is 'std::unique_ptr<IMonsterDead>&'
monster->OnDead();
}
return 0;
}
Iterators are an interesting abstraction, in this case to be reduced to pointers.
Either you receive the pointer to the element or you get an invalid end.
You can use it as a pointer: (*result)->func();
You can also use it to create a new variable:
IMonsterDead &m = **result;
m.func();
This should give the same assembly, both possible.
I'm trying to define template class inside of nontemplate class, below you may see the code of what i'm actually trying to do (it doesn't compilable for obvious reason). The main question is how can I realize that using C++11 (preferable) or C++14?
Actually I've got solution using std::variant or same function from the BOOST library, but I need to know another way to solve that.
I found old similar question, and answer by Anne Quinn sounds valuable (he suggest to declare subclasses for each type I need), but how to apply that in code right?
Code:
#include <vector>
#include <cstdint>
enum Type {
INT16,
UINT32
};
template<typename T>
class Buffer {
public:
Buffer(uint32_t paramSize) {
buffer.resize(paramSize);
}
private:
std::vector<T> buffer;
};
class Foo {
public:
Foo(Type paramType, uint32_t paramSize) {
switch(paramType) {
case UINT32:
buffer = Buffer<uint32_t>(paramSize);
break;
case INT16:
buffer = Buffer<int16_t>(paramSize);
break;
}
}
private:
Buffer buffer;
};
int main() {
Foo var(INT16, 30);
return 0;
}
UPD1: answer by #user2308211 seems work, but I got two problems with that. In case I'm copying object Foo and original object destroys for some reason (moving out of scope for example), copy will stay with pointer to nowhere. Second one is how to retrieve my buffer through Foo class.
UPD2: shared_ptr resolves problem with copying, but then copy will store the same object, in case you wanna modify them independently, use copy constructor as shown in answer. As for access to original buffer, void pointer allow you to retrieve pointer to vector, then you should static_cast it to your type.
Thanks!
Have a base class with all the required functions for Buffer as pure virtual.
#include <vector>
#include <cstdint>
enum Type {
INT16,
UINT32
};
class BufferBase {
public:
virtual void someFn()=0;
virtual ~BufferBase() {}
};
template<typename T>
class Buffer:public BufferBase {
public:
Buffer(uint32_t paramSize) {
buffer.resize(paramSize);
}
void someFn() override {
//functionality.
}
~Buffer() {}
private:
std::vector<T> buffer;
};
class Foo {
public:
Foo(Type paramType, uint32_t paramSize) {
this->bufferType = paramType;
switch(paramType) {
case UINT32:
buffer = new Buffer<uint32_t>(paramSize);
break;
case INT16:
buffer = new Buffer<int16_t>(paramSize);
break;
}
}
~Foo() {
delete this->buffer;
}
Foo &operator=(const Foo &other) {
this->bufferType = other.bufferType;
switch(bufferType) {
case UINT32:
buffer = new Buffer<uint32_t>(*static_cast<Buffer<uint32_t>*>(other.buffer));
break;
case INT16:
buffer = new Buffer<int16_t>(*static_cast<Buffer<int16_t>*>(other.buffer));
break;
}
return *this;
}
Foo(const Foo &other) {
*this=other;
}
private:
BufferBase *buffer;
Type bufferType;
};
int main() {
Foo var1(INT16, 30), var2(UINT32, 25);
var1 = var2;
return 0;
}
EDIT: I've updated the answer with a copy constructor.
I have a struct as follows:
struct P
{
enum {INT, FLOAT, BOOLEAN, STRING, ERROR} tag;
union
{
int a;
float b;
bool c;
const char* d;
};
};
I'm using cereal library to serialize this and cereal does not support raw pointers. I'm replacing const char* d with const shared_ptr<char> d. I'm facing 3 issues:
Converting char* to shared_ptr:
char* x = //first element of char array
d = shared_ptr<char> (x); // is this the right way?
Handling assignments like:
string s = "hello";
d = s.c_str(); // how to convert the c_str() to shared_ptr<char>?
From what I've read, shared_ptr seems to handle pointers very different from raw pointers. Will I be able to use this shared_ptr as a character array safely without any side effects?
First thing to say is that you're using a union. Unions in c++ are really hard to get right. Do you really need a union?
If you really need a union, use boost::variant instead. It solves all the complexity for you.
Next, we're using C++ - not C. Let's act like it. Get rid of that const char *. It's a landmine. That's why cereal does not support it. They're doing the right thing. Replace it with what it is. A std::string.
EDIT:
OK. You asked for it. Here is a solution using a discriminated union.
Now, remember I said that unions are hard to get right in c++?
I've been writing c++ almost every day for the past 15 (20?) years. I'm an avid follower of the progress of the standard, I always use the latest tools and I demand that people in my team know the language and the standard library inside out... and I am not yet sure that this solution is fully robust. I would need to spend a day writing tests to be really sure... because discriminated unions are really hard to get right.
EDIT2:
fixed the 'construct from const char*' bug (told you it was hard...)
Are you sure you would rather not use boost::variant?
No? ok then:
#include <iostream>
#include <string>
struct error_type {};
static constexpr error_type as_error = error_type {};
struct P
{
enum {
INT, FLOAT, BOOLEAN, STRING, ERROR
} _tag;
union data
{
data() {}
~data() {} // define a destructor that does nothing. We need to handle destruction cleanly in P
int a;
double b; // use doubles - all calculation are performed using doubles anyway
bool c = false; // provide a default constructor
std::string d; // string or error
} _data;
// default constructor - we must initialised the union and the tag.
P() : _tag { BOOLEAN }, _data {} {};
// offer constructors in terms of the various data types we're storing. We'll need to descriminate
// between strings and errors...
P(int a) : _tag (INT) {
_data.a = a;
}
P(double b) : _tag (FLOAT) {
_data.b = b;
}
P(bool c) : _tag (BOOLEAN) {
_data.c = c;
}
P(std::string s) : _tag(STRING)
{
new (std::addressof(_data.d)) std::string(std::move(s));
}
// provide a const char* constructor... because const char* converts to bool
// more readily than it does to std::string (!!!)
P(const char* s) : P(std::string(s)) {}
P(std::string s, error_type) : _tag(ERROR)
{
new (std::addressof(_data.d)) std::string(std::move(s));
}
// destructor - we *must* handle the case where the union contains a string
~P() {
destruct();
}
// copy constructor - we must initialise the union correctly
P(const P& r)
: _tag(r._tag)
{
copy_construct(r._data);
}
// move constructor - this will be particularly useful later...
P(P&& r) noexcept
: _tag(r._tag)
{
steal_construct(std::move(r._data));
}
// assignment operator in terms of constructor
P& operator=(const P& p)
{
// this line can throw
P tmp(p);
// but these lines will not
destruct();
steal_construct(std::move(tmp._data));
return *this;
}
// move-assignment in terms of noexcept functions. Therefore noexcept
P& operator==(P&& r) noexcept
{
destruct();
_tag = r._tag;
steal_construct(std::move(r._data));
return *this;
}
// don't define swap - we have a nothrow move-assignment operator and a nothrow
// move constructor so std::swap will be optimal.
private:
// destruct our union, using our tag as the type switch
void destruct() noexcept
{
using namespace std;
switch (_tag) {
case STRING:
case ERROR:
_data.d.~string();
default:
break;
}
}
/// construct our union from another union based on our tag
void steal_construct(data&& rd) noexcept
{
switch(_tag) {
case INT:
_data.a = rd.a;
break;
case FLOAT:
_data.b = rd.b;
break;
case BOOLEAN:
_data.c = rd.c;
break;
case STRING:
case ERROR:
new (std::addressof(_data.d)) std::string(std::move(rd.d));
break;
}
}
// copy the other union's data based on our tag. This can throw.
void copy_construct(const data& rd)
{
switch(_tag) {
case INT:
_data.a = rd.a;
break;
case FLOAT:
_data.b = rd.b;
break;
case BOOLEAN:
_data.c = rd.c;
break;
case STRING:
case ERROR:
new (std::addressof(_data.d)) std::string(rd.d);
break;
}
}
public:
// finally, now all that union boilerplate malarkey is dealt with, we can add some functionality...
std::string report() const {
using namespace std::string_literals;
using std::to_string;
switch (_tag)
{
case INT:
return "I am an int: "s + to_string(_data.a);
case FLOAT:
return "I am a float: "s + to_string(_data.b);
case BOOLEAN:
return "I am a boolean: "s + (_data.c ? "true"s : "false"s);
case STRING:
return "I am a string: "s + _data.d;
case ERROR:
return "I am an error: "s + _data.d;
}
}
};
int main()
{
P p;
std::cout << "p is " << p.report() << std::endl;
auto x = P("hello");
std::cout << "x is " << x.report() << std::endl;
auto y = P("goodbye", as_error);
std::cout << "y is " << y.report() << std::endl;
auto z = P(4.4);
std::cout << "z is " << z.report() << std::endl;
return 0;
}
expected results:
p is I am a boolean: false
x is I am a string: hello
y is I am an error: goodbye
z is I am a float: 4.400000
Replacing char * with shared_ptr<char>, as requested by the question title, can be made to compile (with the help of a custom deleter), but it's almost never useful, and will almost certainly not do the right thing in the context of cereal. Since cereal understands standard library types such as strings, why not just use them directly?
Since unions and non-POD types don't really mix, as aptly demonstrated by Richard, you can convert the union into inherited classes with a virtual member function to discriminate the type:
struct P {
enum tag_type {INT, FLOAT, BOOLEAN, STRING, ERROR };
virtual tag_type get_tag() const = 0;
// allow subclsas deletion through base class pointer
virtual ~P() {}
};
struct PInt: public P {
tag_type get_tag() { return INT; }
int a;
};
struct PFloat: public P {
tag_type get_tag() { return FLOAT; }
float b;
};
struct PBool: public P {
tag_type get_tag() { return BOOL; }
bool c;
};
struct PStr: public P {
tag_type get_tag() { return STRING; }
std::string d;
};
struct PError: public P {
tag_type get_tag() { return ERROR; }
};
The code you will end up with is still clunky, but it will handle non-PODs easily and robustly. Using it would boil down to replacing old code that looked like this:
void process(P& x)
switch (x.tag) {
case INT:
// use x._data.a
...
}
}
...with code that uses the get_tag() virtual member to get the tag, and dynamic_cast to access the attributes of the final class:
void process(P& x)
{
switch (x.get_tag()) {
case P::INT:
PInt &intx = dynamic_cast<PInt&>(x);
// ...use intx.a
// case P::FLOAT, etc.
}
}
With this setup, you can use CEREAL_REGISTER_TYPE(Pint), to declare your subclasses to cereal. The library will then use run-time type information to correctly serialize pointers to P.
Basically I want MyClass that holds a Hashmap that maps Field name(string) to ANY type of
Value.. For this purpose I wrote a separate MyField class that holds the type & value information..
This is what I have so far:
template <typename T>
class MyField {
T m_Value;
int m_Size;
}
struct MyClass {
std::map<string, MyField> fields; //ERROR!!!
}
But as you can see, the map declaration fails because I didn't provide the type parameter for MyField...
So I guess It has to be something like
std::map< string, MyField<int> > fields;
or
std::map< string, MyField<double> > fields;
But obviously this undermines my whole purpose, because the declared map can only hold MyField of a specific type.. I want a map that can hold ANY type of MyField clas..
Is there any way I can achieve this..?
This is plain in C++ 17. Use std::map + std::any + std::any_cast:
#include <map>
#include <string>
#include <any>
int main()
{
std::map<std::string, std::any> notebook;
std::string name{ "Pluto" };
int year = 2015;
notebook["PetName"] = name;
notebook["Born"] = year;
std::string name2 = std::any_cast<std::string>(notebook["PetName"]); // = "Pluto"
int year2 = std::any_cast<int>(notebook["Born"]); // = 2015
}
Blindy's answer is very good (+1), but just to complete the answer: there is another way to do it with no library, by using dynamic inheritance:
class MyFieldInterface
{
int m_Size; // of course use appropriate access level in the real code...
~MyFieldInterface() = default;
}
template <typename T>
class MyField : public MyFieldInterface {
T m_Value;
}
struct MyClass {
std::map<string, MyFieldInterface* > fields;
}
Pros:
it's familiar to any C++ coder
it don't force you to use Boost (in some contexts you are not allowed to);
Cons:
you have to allocate the objects on the heap/free store and use reference semantic instead of value semantic to manipulate them;
public inheritance exposed that way might lead to over-use of dynamic inheritance and a lot of long-term issues related to your types really being too inter-dependent;
a vector of pointers is problematic if it have to own the objects, as you have to manage destruction;
So use boost::any or boost::variant as default if you can, and consider this option only otherwise.
To fix that last cons point you could use smart pointers:
struct MyClass {
std::map<string, std::unique_ptr<MyFieldInterface> > fields; // or shared_ptr<> if you are sharing ownership
}
However there is still a potentially more problematic point:
It forces you to create the objects using new/delete (or make_unique/shared). This mean that the actual objects are created in the free store (the heap) at any location provided by the allocator (mostly the default one). Therefore, going though the list of objects very often is not as fast as it could be because of cache misses.
If you are concerned with performance of looping through this list very often as fast as possible (ignore the following if not), then you'd better use either boost::variant (if you already know all the concrete types you will use) OR use some kind of type-erased polymorphic container.
The idea is that the container would manage arrays of objects of the same type, but that still expose the same interface. That interface can be either a concept (using duck-typing techniques) or a dynamic interface (a base class like in my first example).
The advantage is that the container will keep same-type objects in separate vectors, so going through them is fast. Only going from one type to another is not.
Here is an example (the images are from there): http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html
However, this technique loose it's interest if you need to keep the order in which the objects are inserted.
In any way, there are several solutions possible, which depends a lot on your needs. If you have not enough experience with your case, I suggest using either the simple solution I first explained in my example or boost::any/variant.
As a complement to this answer, I want to point very good blog articles which summarize all C++ type-erasure techniques you could use, with comments and pros/cons:
http://talesofcpp.fusionfenix.com/post-16/episode-nine-erasing-the-concrete
http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/
Use either boost::variant (if you know the types you can store, it provides compile time support) or boost::any (for really any type -- but that's kind of unlikely to be the case).
http://www.boost.org/doc/libs/1_55_0/doc/html/variant/misc.html#variant.versus-any
Edit: I cannot emphasize enough that although rolling your own solution might seem cool, using a complete, proper implementation will save you a lot of headache in the long run. boost::any implements RHS copy constructors (C++11), both safe (typeid()) and unsafe (dumb casts) value retrievals, with const corectness, RHS operands and both pointer and value types.
That's true in general, but even more so for low level, base types you build your entire application on.
class AnyBase
{
public:
virtual ~AnyBase() = 0;
};
inline AnyBase::~AnyBase() {}
template<class T>
class Any : public AnyBase
{
public:
typedef T Type;
explicit Any(const Type& data) : data(data) {}
Any() {}
Type data;
};
std::map<std::string, std::unique_ptr<AnyBase>> anymap;
anymap["number"].reset(new Any<int>(5));
anymap["text"].reset(new Any<std::string>("5"));
// throws std::bad_cast if not really Any<int>
int value = dynamic_cast<Any<int>&>(*anymap["number"]).data;
C++17 has a std::variant type that has facilities for holding different types much better than a union.
For those not on C++17, boost::variant implements this same mechanism.
For those not using boost, https://github.com/mapbox/variant implements a much lighter version of variant for C++11 and C++14 that looks very promising, well documented, lightweight, and has plenty of usage examples.
You could also use a void* and cast the value back to the correct type using reinterpret_cast. Its a technique often used in C in callbacks.
#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdint> // Needed for intptr_t
using namespace std;
enum TypeID {
TYPE_INT,
TYPE_CHAR_PTR,
TYPE_MYFIELD
};
struct MyField {
int typeId;
void * data;
};
int main() {
std::unordered_map<std::string, MyField> map;
MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) };
char cstr[] = "Jolly good";
MyField aCString = { TYPE_CHAR_PTR, cstr };
MyField aStruct = { TYPE_MYFIELD, &anInt };
map.emplace( "Int", anInt );
map.emplace( "C String", aCString );
map.emplace( "MyField" , aStruct );
int intval = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data));
const char *cstr2 = reinterpret_cast<const char *>( map["C String"].data );
MyField* myStruct = reinterpret_cast<MyField*>( map["MyField"].data );
cout << intval << '\n'
<< cstr << '\n'
<< myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl;
}
This is a naive way of doing it. Of course, you can add wrappers to void the some boiler plate code.
#include <iostream>
#include <memory>
#include <map>
#include <vector>
#include <cassert>
struct IObject
{
virtual ~IObject() = default;
};
template<class T>
class Object final : public IObject
{
public:
Object(T t_content) : m_context(t_content){}
~Object() = default;
const T& get() const
{
return m_context;
}
private:
T m_context;
};
struct MyClass
{
std::map<std::string, std::unique_ptr<IObject>> m_fields;
};
int main()
{
MyClass yourClass;
// Content as scalar
yourClass.m_fields["scalar"] = std::make_unique<Object<int>>(35);
// Content as vector
std::vector<double> v{ 3.1, 0.042 };
yourClass.m_fields["vector"] = std::make_unique<Object<std::vector<double>>>(v);
auto scalar = dynamic_cast<Object<int>*>(yourClass.m_fields["scalar"].get())->get();
assert(scalar == 35);
auto vector_ = dynamic_cast<Object<std::vector<double>>*>(yourClass.m_fields["vector"].get())->get();
assert(v == vector_);
return 0;
}
Work in progress. The advantage this method has is that you don't have to cast anything when doing assignment, or any of the features listed below.
As of now it can:
store non-container literal types (const char*, double, int, float, char, bool)
output value for corresponding key with ostream operator
reassign the value of an existing key
add a new key:value pair using the append method only, key cannot be the same, or else you get an error message
add literals of the same type with the + operator
In the code, I have demonstrated in the main function what it can currently do.
/*
This program demonstrates a map of arbitrary literal types implemented in C++17, using any.
*/
#include <vector>
#include <any>
#include <utility>
#include <iostream>
using namespace std;
class ArbMap
{
public:
ArbMap() : vec({}), None("None") {} //default constructor
ArbMap(const vector < pair<any,any> > &x) //parametrized constructor, takes in a vector of pairs
: vec(x), None("None") {}
//our conversion function, this time we pass in a reference
//to a string, which will get updated depending on which
//cast was successful. Trying to return values is ill-advised
//because this function is recursive, so passing a reference
//was the next logical solution
void elem(any &x, string &temp, int num=0 )
{
try
{
switch (num)
{
case 0:
any_cast<int>(x);
temp = "i";
break;
case 1:
any_cast<double>(x);
temp = "d";
break;
case 2:
any_cast<const char*>(x);
temp = "cc";
break;
case 3:
any_cast<char>(x);
temp = "c";
break;
case 4:
any_cast<bool>(x);
temp = "b";
break;
case 5:
any_cast<string>(x);
temp = "s";
break;
}
}
catch(const bad_cast& e)
{
elem(x,temp,++num);
}
}
//returns size of vector of pairs
size_t size()
{
return vec.size();
}
/* Uses linear search to find key, then tries to cast
all the elements into the appropriate type. */
any& operator[](any key)
{
ArbMap temp;
string stemp;
for (size_t i = 0; i<vec.size(); ++i)
{
temp.elem(vec[i].first,stemp);
if (stemp=="i")
{
try
{
any_cast<int>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<int>(key)==any_cast<int>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="d")
{
try
{
any_cast<double>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<double>(key)==any_cast<double>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="cc")
{
try
{
any_cast<const char*>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<const char*>(key)==any_cast<const char*>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="c")
{
try
{
any_cast<char>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<char>(key)==any_cast<char>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="b")
{
try
{
any_cast<bool>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<bool>(key)==any_cast<bool>(vec[i].first))
{
return vec[i].second;
}
}
}
//vec.push_back({key,None});
throw -1;
//return None;
}
void print();
void append(any key, any value);
private:
vector < pair<any,any> > vec;
any None;
};
ostream& operator<<(ostream& out, any a)
{
ArbMap temp; //should be updated to be a smart pointer?
string stemp;
temp.elem(a,stemp); //stemp will get updated in the elem function
//The "if else-if ladder" for casting types
if (stemp=="i") out << any_cast<int>(a);
else if (stemp=="d") out << any_cast<double>(a);
else if (stemp=="cc") out << any_cast<const char*>(a);
else if (stemp=="c") out << any_cast<char>(a);
else if (stemp=="b")
{
if (any_cast<bool>(a)==1)
out << "true";
else
out << "false";
}
else if (stemp=="s") out << any_cast<string>(a);
return out;
}
any operator+(any val1, any val2)
{
ArbMap temp;
string stemp1, stemp2;
temp.elem(val1,stemp1);
temp.elem(val2,stemp2);
try
{
if (stemp1 != stemp2)
throw -1;
if (stemp1 == "i")
{
return any_cast<int>(val1)+any_cast<int>(val2);
}
else if (stemp1 == "d")
{
return any_cast<double>(val1)+any_cast<double>(val2);
}
else if (stemp1 == "cc")
{
return string(any_cast<const char*>(val1))+string(any_cast<const char*>(val2));
}
else if (stemp1 == "c")
{
return string{any_cast<char>(val1)}+string{any_cast<char>(val2)};
}
else if (stemp1=="b")
{
return static_cast<bool>(any_cast<bool>(val1)+any_cast<bool>(val2));
}
}
catch (int err)
{
cout << "Bad cast! Operands must be of the same 'type'.\n";
}
return val1;
}
void ArbMap::print()
{
cout << '\n';
for (size_t i = 0; i<vec.size(); ++i)
{
cout << vec[i].first << ": " << vec[i].second << '\n';
}
cout << '\n';
}
void ArbMap::append(any key, any value)
{
try
{
(*this)[key];
throw "Already exists!";
}
catch(int error)
{
vec.push_back({key,value});
}
catch(const char* error)
{
cout << "ArbMap::append failed, key already exists!\n";
}
}
int main() {
ArbMap s({{1,2},{"aaa",1.2},{'c',33.3},{"what","this is awesome"}, {true, false}});
cout << s[1] << '\n' << s["aaa"] << '\n' << s['c']
<< '\n' << s["what"] << '\n'
//Uncomment the line below and runtime error will occur, as
//entry is not in the dictionary
// << s["not in the dictionary bro"] << '\n'
<< s[true] << '\n';
s.print();
s[1] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[2.3] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[1] = 1.2;
s.print();
s.append(2.4,1.2);
//Operator +
cout << s[1]+s[2.4] << '\n';
cout << s["what"] + s[2.3] << '\n';
s.append('d','a');
cout << s['c'] << '\n';
cout << s[2.4]+ s["aaa"]+ s['c'] + s['c'] + s['c'] << '\n';
cout << s[true]+s[true] << '\n';
return 0;
}
This question already has answers here:
Closed 10 years ago.
Edit Solution::
In fact, i juste forget the placment new in the copy constructor ><"
Question:
I have a weird problem. After having tried for a long momnet origin I found masi does not understand.
If someone can explain to me why.
My class:
class B; //on other file
class A {
public:
A(int type) : type(type)
{
switch(type)
{
case TOKEN:
{
for(int i=0;i<4;++i)
new(&token.h[i].link) shared_ptr<B>; //< init the ptr on the addr (because of union)
}break;
case OTHER: {}break;
}
}
~A()
{
switch(type)
{
case TOKEN:
{
for(int i=0;i<4;++i)
{
/*option 1*/ token.h[i].link.~shared_pt<B>(); //< Make seg fault
/*option 2*/ token.h[i].link.reset(); //< ok
}
}break;
case OTHER: {}break;
}
}
}
enum {TOKEN=0,OTHER} type;
union {
struct {
double score;
struct {
std::shared_ptr<B> link;
double to_find;
} h [4];
}token;
struct {
//else
} other;
}
};
My code:
void f()
{
vector<A> vec;
A tmp = A(A::TOKEN);
vec.emplace_back(tmp);
}
Option 1: this causes an error when leaving f;
option 2: Ok but ~shared_ptr() is not call, so it make memory leak, right?
If you have an idea that could help me understand who is wrong.
Edit:
I use C++11 with gcc.4.6.3 on Ubuntu 12.04x86.
Original code:
class stack_token {
public:
stack_token();
stack_token(const stack_token& other);
stack_token(const int i,Parser::peptide::peak* data); //peak
stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak); //aa
stack_token(const int i); //aa pour boucher un trou
stack_token(const double score); //HEADER
~stack_token();
stack_token& operator=(const stack_token& other);
inline stack_token* get_peak_stack_NULL() {
stack_token* res = aa_token.pt_data;
aa_token.pt_data=NULL;
return res;
};
void __print__() const;
enum Type {UNKNOW=-1,AA_TOKEN=0,AA_HOLD_TOKEN,/*AA_LIST,*/PEAK_TOKEN, HEADER_TOKEN} type;
union {
struct {
int index;
double error;
stack_token* pt_data;
} aa_token;
struct{
double error;
stack_token* pt_data;
std::vector<int> aa_index;
} aa_hold_token;
struct {
int index;
Parser::peptide::peak* pt_data;
} peak_token;
struct {
double score;
struct {
std::shared_ptr<std::list<list_arg> > link;
double to_find;
} holds [Parser::peptide::SIZE];
} header_token;
};
};
stack_token::~stack_token()
{
switch(type)
{
case AA_TOKEN:
{
if(aa_token.pt_data != NULL)
delete aa_token.pt_data;
}break;
case AA_HOLD_TOKEN :
{
aa_hold_token.aa_index.~vector<int>();
}break;
case PEAK_TOKEN :
{
}break;
case HEADER_TOKEN :
{
for (int i=0;i<Parser::peptide::SIZE;++i)
header_token.holds[i].link.reset();//~shared_ptr<std::list<list_arg> >();
}break;
default : break;
}
};
stack_token::stack_token()
{
this->type = UNKNOW;
};
stack_token::stack_token(const int i,Parser::peptide::peak* data) //peak
{
this->type=PEAK_TOKEN;
peak_token.index = i;
peak_token.pt_data = data;
};
stack_token::stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak) //aa
{
this->type=AA_TOKEN;
aa_token.error =e;
aa_token.index = i;
aa_token.pt_data = peak;
};
stack_token::stack_token(const int i)
{
this->type=AA_HOLD_TOKEN;
aa_hold_token.error = 0;
aa_hold_token.pt_data = this;
new(&aa_hold_token.aa_index) vector<int>();
};
stack_token::stack_token(const double score) //HEADER
{
this->type = HEADER_TOKEN;
header_token.score = score;
for (int i=0;i<Parser::peptide::SIZE;++i)
new (&header_token.holds[i].link) shared_ptr<list<list_arg> >;
#warning "add to_find init"
};
Code that fail:
void save_stack(const std::list<stack_token*>& search, std::list<std::vector<stack_token> >& res)
{
vector<AnalyseurPeptide::stack_token> l;
auto i=search.begin();
auto end = search.end();
stack_token tmp = stack_token(0.f); /* if I remove this */
l.emplace_back(tmp); /* and this, all is ok */
while(i!=end)
{
l.emplace_back(**i); //< fail here
++i;
}
res.emplace_back(l);
}
If you're compiling with C++03, the code is illegal, because
C++03 doesn't allow types with non-trivial default constructors,
copy constructors, assignment operators or destructors in
a union. With C++11, the code is illegal, because if the union
contains any of the above, the compiler deletes the
corresponding member of the union. So your union has no default
constructor, copy constructor, assignment or destructor. Which
means you can't instantiate it, or use it in any way. And which
means that the default constructor needed by A::A(int) doesn't
exist, and that the compile should complain when you define this
function (or any constructor of A).
If the compiler compiles such code, it means that the compiler
doesn't implement the new union stuff correctly, and thus, that
you cannot use it.
With regards to what actually happens: I suspect that the
compiler is using bitwise copy in the copy constructor of A
(rather than refusing to generate it). vec.emplace_back(tmp)
uses the copy constructor to create the new element in vec.
Bitwise copy means that you end up with two instances of
a shared_ptr which point to the same object, but which both
have a count of 1. The first one destructs correctly, and the
second accesses deleted memory. Boom.
The simplest way to solve your problem is to use
boost::variant (which means defining the struct in the union
somewhere outside of the union, and giving them a name). If for
some reason you cannot use Boost, it's relatively trivial to
implement by hand, along the lines of what you are doing. In
the union itself, you just have unsigned char token[
sizeof(TokenType) ]; etc., for each non-POD member, with some
additional members if necessary to ensure alignment (on most
processors, a double will do the trick). You then use
reinterpret_cast on the name of the array to get a pointer to
the desired type, placement new to initialize it, and explicit
destruction to destruct it, much along the lines you've done.
And you implement a copy constructor and an assignment
operator that work, and take into account the types as well.
(It's not that difficult. I've done it one or two times: for
tokens in a parser, for modeling tables which we get from Excel,
etc.)
Technical problems:
union (don't),
uninitialized,
rule of three (not taking properly charge of copying)
Design problems:
Representing types as numbers. Represent types as types.
Keep the knowledge you gained from writing that code, and start from scratch again.
Very little more can be meaningfully said until you post the real code (e.g. swithc will never compile: what you posted is not the real code).