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.
Related
I'd like to simplify the code I write in my application that handles mutiple data structure types but with a common header. Given something like this:
enum class MyType {
Foo = 100,
Bar = 200,
};
struct Hdr {
MyType type;
};
struct Foo {
Hdr hdr;
int x;
int y;
int z;
};
struct Bar {
Hdr hdr;
double value;
double ratio;
};
void process(const Foo *ptr)
{
// process Foo here
}
void process(const Bar *ptr)
{
// process Bar here
}
extern void *getData();
int main()
{
const void *pv = getData();
auto pHdr = static_cast<const Hdr *>(pv);
switch (pHdr->type) {
case MyType::Foo: process(static_cast<const Foo *>(pv)); break;
case MyType::Bar: process(static_cast<const Bar *>(pv)); break;
default: throw "Unknown";
}
return 0;
}
Ideally I'd like to replace the switch statement above with something like:
process(multi_cast<pHdr->type>(pv);
I'm perfectly okay with having to write statements like this to get it to work:
template<MyType::Foo>
const Foo *multi_cast(void *p)
{
return static_cast<const Foo *>(p);
}
template<MyType::Bar>
const Bar *multi_cast(void *p)
{
return static_cast<const Bar *>(p);
}
But I cannot write a template where the template parameter is a enum (or an int for that matter)
Have I just looked at this for so long that I cannot see an answer?
Or is there just no other way to do it?
There is just no other way to do it.
As the comments have pointed out, since the type is stored in the header at run-time, you have to have some kind of run-time lookup; no amount of templates or overload resolution can help you since all of that is at compile-time.
You can abstract the lookup as much as you want, but you can only replace the switch statement with another type of lookup, and you can only decrease performance the further you get away from a simple switch/lookup table.
For example, you could start with something like this and go nuts:
#include <iostream>
#include <cassert>
enum class Type {
FOO,
BAR,
NUM_
};
struct Header {
Header(Type t)
: type(t)
{}
Type type;
};
struct Foo {
Foo(int x, int y, int z)
: header(Type::FOO), x(x), y(y), z(z)
{}
Header header;
int x;
int y;
int z;
};
struct Bar {
Bar(double value, double ratio)
: header(Type::BAR), value(value), ratio(ratio)
{}
Header header;
double value;
double ratio;
};
static inline void process(Foo*) {
printf("processing foo...\n");
}
static inline void process(Bar*) {
printf("processing bar...\n");
}
using ProcessFunc = void(*)(void*);
static ProcessFunc typeProcessors[(size_t)Type::NUM_] = {
[](void* p) { process((Foo*)p); },
[](void* p) { process((Bar*)p); },
};
static void process(void* p) {
Type t = ((Header*)p)->type;
assert((size_t)t < (size_t)Type::NUM_ && "Invalid Type.");
typeProcessors[(size_t)t](p);
}
static void* get_foo()
{
static Foo foo(0, 0, 0);
return &foo;
}
static void* get_bar()
{
static Bar bar(0.0, 0.0);
return &bar;
}
int main() {
Foo foo(0, 0, 0);
Bar bar(0.0, 0.0);
process(&foo);
process(&bar);
process(get_foo());
process(get_bar());
return 0;
}
but then you're only getting cute and most likely slower. You might as well just put the switch in process(void*)
If you aren't serializing your data(doubtful), are mostly processing one type at a time, and want an OO solution(I wouldn't), you could return a base type that your types inherit from and add a pure virtual process function like so:
struct Type {
virtual void process() = 0;
virtual ~Type() {}
};
struct Foo : Type {
int x = 0;
int y = 0;
int z = 0;
virtual void process() override {
printf("processing foo...\n");
}
};
struct Bar : Type {
double value = 0.0;
double ratio = 0.0;
virtual void process() override {
printf("processing bar...\n");
}
};
static Type* get_foo() {
static Foo foo;
return &foo;
}
static Type* get_bar() {
static Bar bar;
return &bar;
}
int main() {
Foo foo;
Bar bar;
foo.process();
bar.process();
get_foo()->process();
get_bar()->process();
return 0;
}
I would stick with the switch, but I would keep the values of Type::FOO and Type::BAR the default 0 and 1. If you mess with the values too much, the compiler might decide to implement the switch as a bunch of branches as opposed to a lookup table.
You have two issues:
Converting a runtime value (your "type") into a compile time determined type (with associated behavior).
"Unifying" the possible different types to a single (statically at compile time known) type.
Point 2 is what inheritance together with virtual member functions are for:
struct Thing {
virtual void doStuff() const = 0;
virtual ~Thing() {}
};
struct AThing : Thing {
void doStuff() const override { std::cout << "A"; }
};
struct BThing : Thing {
void doStuff() const override { std::cout << "B"; }
};
Point 1 is usually tackled by creating some kind of "factory" mechanism, and then dispatching based on the runtime value to one of those factories. First, the factories:
Thing * factoryA() { return new AThing(); }
Thing * factoryB() { return new BThing(); }
Thing * factory_failure() { throw 42; }
The "dispatching" (or "choosing the right factory") can be done in different ways, one of those being your switch statement (fast, but clumsy), linear search through some container/array (easy, slow) or by lookup in a map (logarithmic - or constant for hashing based maps).
I chose a (ordered) map, but instead of using std::map (or std::unordered_map) I use a (sorted!) std::array to avoid dynamic memory allocation:
// Our "map" is nothing more but an array of key value pairs.
template <
typename Key,
typename Value,
std::size_t Size>
using cmap = std::array<std::pair<Key,Value>, Size>;
// Long type names make code hard to read.
template <
typename First,
typename... Rest>
using cmap_from =
cmap<typename First::first_type,
typename First::second_type,
sizeof...(Rest) + 1u>;
// Helper function to avoid us having to specify the size
template <
typename First,
typename... Rest>
cmap_from<First, Rest...> make_cmap(First && first,
Rest && ... rest) {
return {std::forward<First>(first), std::forward<Rest>(rest)...};
}
Using std::lower_bound I perform a binary search on this sorted array (ehm "map"):
// Binary search for lower bound, check for equality
template <
typename Key,
typename Value,
std::size_t Size>
Value get_from(cmap<Key,Value,Size> const & map,
Key const & key,
Value alternative) {
assert(std::is_sorted(std::begin(map), std::end(map),
[](auto const & lhs, auto const & rhs) {
return lhs.first < rhs.first; }));
auto const lower = std::lower_bound(std::begin(map), std::end(map),
key,
[](auto const & pair, auto k) {
return pair.first < k; });
if (lower->first == key) {
return lower->second;
} else {
// could also throw or whatever other failure mode
return alternative;
}
}
So that, finally, I can use a static const map to get a factory given some runtime value "type" (or choice, as I named it):
int main() {
int const choices[] = {1, 10, 100};
static auto const map =
make_cmap(std::make_pair(1, factoryA),
std::make_pair(10, factoryB));
try {
for (int choice : choices) {
std::cout << "Processing choice " << choice << ": ";
auto const factory = get_from(map, choice, factory_failure);
Thing * thing = factory();
thing->doStuff();
std::cout << std::endl;
delete thing;
}
} catch (int const & value) {
std::cout << "Caught a " << value
<< " ... wow this is evil!" << std::endl;
}
}
(Live on ideone)
The initialization of that "map" could probably made constexpr.
Of course instead of raw pointers (Thing *) you should use managed pointers (like std::unique_ptr). Further, if you don't want to have your processing (doStuff) as member functions, then just make a single "dispatching" (virtual) member function that calls out to a given function object, passing the own instance (this). With a CRTP base class, you don't need to implement that member function for every one of your types.
You're using something that may be called static (=compile-time) polymorphism. This requires to make such switch statements in order to convert the run-time value pHrd->dtype to one of the compile-time values handles in the case clauses. Something like your
process(multi_cast<pHdr->type>(pv);
is impossible, since pHdr->type is not known at compile time.
If you want to avoid the switch, you can use ordinary dynamic polymorphism and forget about the enum Hdr, but use a abstract base class
struct Base {
virtual void process()=0;
virtual ~Base() {}
};
struct Foo : Base { /* ... */ };
struct Bar : Base { /* ... */ };
Base*ptr = getData();
ptr->process();
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>();
}
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;
}
I have an enum class with two values, and I want to create a method which receives a value
and returns the other one. I also want to maintain type safety(that's why I use enum class instead of enums).
http://www.cplusplus.com/doc/tutorial/other_data_types/ doesn't mention anything about methods
However, I was under the impression that any type of class can have methods.
No, they can't.
I can understand that the enum class part for strongly typed enums in C++11 might seem to imply that your enum has class traits too, but it's not the case. My educated guess is that the choice of the keywords was inspired by the pattern we used before C++11 to get scoped enums:
class Foo {
public:
enum {BAR, BAZ};
};
However, that's just syntax. Again, enum class is not a class.
While the answer that "you can't" is technically correct, I believe you may be able to achieve the behavior you're looking for using the following idea:
I imagine that you want to write something like:
Fruit f = Fruit::Strawberry;
f.IsYellow();
And you were hoping that the code looks something like this:
enum class Fruit : uint8_t
{
Apple,
Pear,
Banana,
Strawberry,
bool IsYellow() { return this == Banana; }
};
But of course, it doesn't work, because enums can't have methods (and 'this' doesn't mean anything in the above context)
However, if you use the idea of a normal class containing a non-class enum and a single member variable that contains a value of that type, you can get extremely close to the syntax/behavior/type safety that you want. i.e.:
class Fruit
{
public:
enum Value : uint8_t
{
Apple,
Pear,
Banana,
Strawberry
};
Fruit() = default;
constexpr Fruit(Value aFruit) : value(aFruit) { }
#if Enable switch(fruit) use case:
// Allow switch and comparisons.
constexpr operator Value() const { return value; }
// Prevent usage: if(fruit)
explicit operator bool() const = delete;
#else
constexpr bool operator==(Fruit a) const { return value == a.value; }
constexpr bool operator!=(Fruit a) const { return value != a.value; }
#endif
constexpr bool IsYellow() const { return value == Banana; }
private:
Value value;
};
Now you can write:
Fruit f = Fruit::Strawberry;
f.IsYellow();
And the compiler will prevent things like:
Fruit f = 1; // Compile time error.
You could easily add methods such that:
Fruit f("Apple");
and
f.ToString();
can be supported.
Concentrating on the description of the question instead of the title a possible answer is
struct LowLevelMouseEvent {
enum Enum {
mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
mouse_event_unknown = 0,
mouse_event_unimplemented,
mouse_event_unnecessary,
mouse_event_move,
mouse_event_left_down,
mouse_event_left_up,
mouse_event_right_down,
mouse_event_right_up,
mouse_event_middle_down,
mouse_event_middle_up,
mouse_event_wheel
};
static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
{
switch (event) {
case mouse_event_unknown: return "unknown";
case mouse_event_unimplemented: return "unimplemented";
case mouse_event_unnecessary: return "unnecessary";
case mouse_event_move: return "move";
case mouse_event_left_down: return "left down";
case mouse_event_left_up: return "left up";
case mouse_event_right_down: return "right down";
case mouse_event_right_up: return "right up";
case mouse_event_middle_down: return "middle down";
case mouse_event_middle_up: return "middle up";
case mouse_event_wheel: return "wheel";
default:
Assert (false);
break;
}
return "";
}
};
There is a pretty compatible ability(§) to refactor an enum into a class without having to rewrite your code, which means that effectively you can do what you were asking to do without too much editing.
(§) as ElementW points out in a comment, type_traits dependent code will not work, so e.g. one cannot use auto, etc. There may be some way of handling such stuff, but in the end one is converting an enum into a class, and it is always a mistake to subvert C++
the enum struct and enum class specifications are about scoping so not part of this.
Your original enum is e.g. 'pet' (this is as an example only!).
enum pet {
fish, cat, dog, bird, rabbit, other
};
(1) You modify that to eg petEnum (so as to hide it from your existing code).
enum petEnum {
fish, cat, dog, bird, rabbit, other
};
(2) You add a new class declaration below it (named with the original enum)
class pet {
private:
petEnum value;
pet() {}
public:
pet(const petEnum& v) : value{v} {} //not explicit here.
operator petEnum() const { return value; }
pet& operator=(petEnum v) { value = v; return *this;}
bool operator==(const petEnum v) const { return value == v; }
bool operator!=(const petEnum v) const { return value != v; }
// operator std::string() const;
};
(3) You can now add whatever class methods you like to your pet class.
eg. a string operator
pet::operator std::string() const {
switch (value) {
case fish: return "fish";
case cat: return "cat";
case dog: return "dog";
case bird: return "bird";
case rabbit: return "rabbit";
case other: return "Wow. How exotic of you!";
}
}
Now you can use eg std::cout...
int main() {
pet myPet = rabbit;
if(myPet != fish) {
cout << "No splashing! ";
}
std::cout << "I have a " << std::string(myPet) << std::endl;
return 0;
}
As mentioned in the other answer, no. Even enum class isn't a class.
Usually the need to have methods for an enum results from the reason that it's not a regular (just incrementing) enum, but kind of bitwise definition of values to be masked or need other bit-arithmetic operations:
enum class Flags : unsigned char {
Flag1 = 0x01 , // Bit #0
Flag2 = 0x02 , // Bit #1
Flag3 = 0x04 , // Bit #3
// aso ...
}
// Sets both lower bits
unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);
// Set Flag3
flags |= Flags::Flag3;
// Reset Flag2
flags &= ~Flags::Flag2;
Obviously one thinks of encapsulating the necessary operations to re-/set single/group of bits, by e.g. bit mask value or even bit index driven operations would be useful for manipulation of such a set of 'flags'.
The c++11 struct/class specification just supports better scoping of enum values for access. No more, no less!
Ways to get out of the restriction you cannot declare methods for enum (classes) are , either to use a std::bitset (wrapper class), or a bitfield union.
unions, and such bitfield unions can have methods (see here for the restrictions!).
I have a sample, how to convert bit mask values (as shown above) to their corresponding bit indices, that can be used along a std::bitset here: BitIndexConverter.hpp
I've found this pretty useful for enhancing readability of some 'flag' decison based algorithms.
It may not fulfill all your needs, but with non-member operators you can still have a lot of fun. For example:
#include <iostream>
enum class security_level
{
none, low, medium, high
};
static bool operator!(security_level s) { return s == security_level::none; }
static security_level& operator++(security_level& s)
{
switch(s)
{
case security_level::none: s = security_level::low; break;
case security_level::low: s = security_level::medium; break;
case security_level::medium: s = security_level::high; break;
case security_level::high: break;
}
return s;
}
static std::ostream & operator<<(std::ostream &o, security_level s)
{
switch(s)
{
case security_level::none: return o << "none";
case security_level::low: return o << "low";
case security_level::medium: return o << "medium";
case security_level::high: return o << "high";
}
}
This allows code like
security_level l = security_level::none;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // not reached
++++l;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // reached: "medium"
Based on jtlim's answer
Idea (Solution)
enum ErrorType: int {
noConnection,
noMemory
};
class Error {
public:
Error() = default;
constexpr Error(ErrorType type) : type(type) { }
operator ErrorType() const { return type; }
constexpr bool operator == (Error error) const { return type == error.type; }
constexpr bool operator != (Error error) const { return type != error.type; }
constexpr bool operator == (ErrorType errorType) const { return type == errorType; }
constexpr bool operator != (ErrorType errorType) const { return type != errorType; }
String description() {
switch (type) {
case noConnection: return "no connection";
case noMemory: return "no memory";
default: return "undefined error";
}
}
private:
ErrorType type;
};
Usage
Error err = Error(noConnection);
err = noMemory;
print("1 " + err.description());
switch (err) {
case noConnection:
print("2 bad connection");
break;
case noMemory:
print("2 disk is full");
break;
default:
print("2 oops");
break;
}
if (err == noMemory) { print("3 Errors match"); }
if (err != noConnection) { print("4 Errors don't match"); }
Yes, they can, but you need to make a wrapper class, for example:
#include <iostream>
using namespace std;
class Selection {
public:
enum SelectionEnum {
yes,
maybe,
iDontKnow,
canYouRepeatTheQuestion
};
Selection(SelectionEnum selection){value=selection;};
string toString() {
string selectionToString[4]={
"Yes",
"Maybe",
"I don't know",
"Can you repeat the question?"
};
return selectionToString[value];
};
private:
SelectionEnum value;
};
int main(){
Selection s=Selection(Selection::yes);
cout<<s.toString()<<endl;
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).