I want to use a scoped enum with a std::get to access an object stored in a std::vector
From Effective Modern C++ I'm using a version of Item 10 to cast the enum type to the underlying_type_t that can be used with std::get
template<typename E>
constexpr auto to_mytype(E enumerator) noexcept
{
return static_cast<std::underlying_type_t<E>>(enumerator);
}
and
enum class my_type{sel_1, sel_2};
I have
std::vector<std::variant<std::unique_ptr<option1>, std::unique_ptr<option2>> my_store;
which is a member of my_class and I want to use the object stored in this vector like so
void my_class::my_function(const my_type selection)
{
std::get<to_mytype(selection)>(my_store[i])->do_work();
}
where selection would be sel_1 or sel_2. If I try this I get
error: selection is not a constant expression
Really I would like to store the type selected within the class and have the user only pass it once, to the constructor of my_class
to select whether an option1 or option2 object to be added to the vector.
Is there a way I can use an enum like this? I feel like I'm missing something quite simple.
There are multiple issues with this code.
You try to use runtime index into std::get that requires compile
time index
You are trying to select in std::variant, while variant does not hold multiple types at once(tuple does). Also note that variant already knows what type it currently holds.
As a bonus problem: IMAO tuple should be rarely used since it is quite ugly from readability perspective, struct with named members is usually much nicer.
Related
I am trying to implement a C++ class which will wrap a value (among other things). This value may be one of a number of types (string, memory buffer, number, vector).
The easy way to implement this would be to do something like this
class A {
Type type;
// Only one of these will be valid data; which one will be indicated by `type` (an enum)
std::wstring wData{};
long dwData{};
MemoryBuffer lpData{};
std::vector<std::wstring> vData{};
};
This feels inelegant and like it wastes memory.
I also tried implementing this as a union, but it came with significant development overhead (defining custom destructors/move constructors/copy constructors), and even with all of those, there were still some errors I encountered.
I've also considered making A a base class and making a derived class for each possible value it can hold. This also feels like it isn't a great way to solve the problem.
My last approach would be to make each member an std::optional, but this still adds some overhead.
Which approach would be the best? Or is there another design that works better than any of these?
Use std::variant. It is typesafe, tested and exactly the right thing for a finite number of possible types.
It also gets rid of the type enum.
class A {
std::variant<std::wstring, long, MemoryBuffer, std::vector<std::wstring>> m_data{}; // default initializes the wstring.
public
template<class T>
void set_data(T&& data) {
m_data = std::forward<T>(data);
}
int get_index() { // returns index of type.
m_data.index();
}
long& get_ldata() {
return std::get<long>(m_data); // throws if long is not the active type
}
// and the others, or
template<class T>
T& get_data() { // by type
return std::get<T>(m_data);
}
template<int N>
auto get_data() { // by index
return std::get<N>(m_data);
}
};
// using:
A a;
a.index() == 0; // true
a.set_data(42);
a.index() == 1; // true
auto l = a.get<long>(); // l is now of type long, has value 42
a.get<long>() = 1;
l = a.get<1>();
PS: This example does not even include the coolest (in my opinion) feature of std::variant: std::visit I am not sure what you want to do with your class, so I cannot create a meaningful example. If you let me know, I will think about it.
You basically want QVariant without the rest of Qt, then :)?
As others have mentioned, you could use std::variant and put using MyVariant = std::variant<t1, t2, ...> in some common header, and then use it everywhere it's called for. This isn't as inelegant as you may think - the specific types to be passed around are only provided in one place. It is the only way to do it without building a metatype machinery that can encapsulate operations on any type of an object.
That's where boost::any comes in: it does precisely that. It wraps concepts, and thus supports any object that implements these concepts. What concepts are required depends on you, but in general you'd want to choose enough of them to make the type usable and useful, yet not too many so as to exclude some types prematurely. It's probably the way to go, you'd have: using MyVariant = any<construct, _a>; then (where construct is a contract list, an example of which is as an example in the documentation, and _a is a type placeholder from boost::type_erasure.
The fundamental difference between std::variant and boost::any is that variant is parametrized on concrete types, whereas any is parametrized on contracts that the types are bound to. Then, any will happily store an arbitrary type that fulfills all of those contracts. The "central location" where you define an alias for the variant type will constantly grow with variant, as you need to encapsulate more type. With any, the central location will be mostly static, and would change rarely, since changing the contract requirements is likely to require fixes/adaptations to the carried types as well as points of use.
I'm aware of c++ templates, which allow you to write code for multiple types, but what if I want to store and access a type dynamically? Why is this so difficult to do in c++?
I would very much prefer to not have to do something like this:
enum SupportedTypes
{
IntType,
FloatType,
StringType
}
template <typename T>
class ClassThing
{
public:
T Value;
SupportedTypes Type;
}
...
//Not sure if you could even access thing->Type, but regardless, you get the idea...
switch (thing->Type)
{
case IntType:
DoSomething(((ClassThing<int>*)thing)->T);
break;
case FloatType:
DoSomething(((ClassThing<float>*)thing)->T);
break;
case StringType:
DoSomething(((ClassThing<string>*)thing)->T);
break;
}
Why doesn't c++ support something like this:
int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;
?
Another question that I have that I'm more optimistic of receiving a good answer to: if you choose to go the templated route, is there any way to maintain the type if you store it generically in a collection? E.g.:
vector<ClassThing> things;
(This will give an "argument list for class template ... is missing" error, by the way.) My guess is that no, this is not possible because the above is not possible.
How do I store and access a type dynamically in c++?
There are many options to pick from:
use runtime polymorphism, where you have a base class that might offer some common functionality and derived classes for each supported type; you often have to make some choices about how "fat" your interface should be (providing base class functions that only work meaningfully for a subset of derived types) vs. forcing the client to use dynamic_cast<> to recover/switch-on the runtime type
a particularly powerful technique is having the derived classes be type-specific instantiations of the same template, as it means you can support arbitrary types parametrically, i.e. if they provide the semantics of usage that the template expects
use a discriminated union (basically, a type identification enum/int alongside a union of the supported types) - std::variant<> is a good choice for this
when creating/storing a value capture you'll necessarily know it's type
you can record both its typeinfo and address, then when accessing the variable later you can use the typeinfo to test whether the object is of a specific type - trying each supported type until a match is found - std::any<> is a good choice for this, or
you can capture an arbitrary set of type-specific operations using function pointers or std::function<>
Why doesn't c++ support something like this:
int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;?
It does, with decltype and auto:
int whatIsThis = 5;
using t = decltype(whatIsThis);
t anotherInt = 5;
auto anotherWhatever = whatIsThis; // another way to create an additional
// variable of the same type
For runtime polymorphism, you might actually want to read up on factories (which create one of many types of object - all derived from some base interface - given some runtime input), and clone functions (which create a copy of a variable of unknown runtime type).
if you choose to go the templated route, is there any way to maintain the type if you store it generically in a collection: vector<ClassThing> things; (This will give an "argument list for class template ... is missing" error, by the way.)
You can't create even a single object from a template without instantiating it, so no there's no way to have an entire vector either. A reasonable approach is to derive the template from a base class and store [smart] pointers or std::reference_wrappers to the base class in the vector.
int x = 5;
decltype(x) y = 4;
auto z = 3;
decltype(a) will give you the type of a. You can then use typedef to store the types, or other functions to remove references from the type if necessary.
For example:
typedef decltype(a) type1;
type1 b = 2 * a;
auto makes you not need to specify the type at all.
The only thing you need is to compile in c++11 mode (-std=c++11) or later.
As for the vector question, decltype will work there too.
I won't steal the answer, but I will provide the method I ended up using for those who are trying to do something similar. (I am writing my own raw serialization and deserialization code with memcpy.) What I had hoped to do was store and maintain various arrangements of types without having to create a bunch of structs or classes, e.g. (from my question):
template <typename T>
class ClassThing
{
public:
T Value;
SupportedTypes Type;
}
//Then store everything in a:
vector<ClassThing> things;
However, attempting to store a templated class in a vector will give an "argument list for class template ... is missing" error, because as Tony D said in his answer, "You can't create even a single object from a template without instantiating it..." I also didn't want to use any external libraries like boost (for variants).
So, I concluded that because I absolutely wanted to use a single collection to store all of the structures, I simply could not use a templated class. Instead, I resolved to use a templated constructor (only) and a void* for the Value, as well as store the type's hash and the number of bytes required for storing/copying the type:
class ClassThing
{
public:
void* Value;
unsigned long long TypeHash;
unsigned long long NumberOfBytes;
template <typename T>
ClassThing(T passedValue)
{
Value = &passedValue;
TypeHash = typeid(passedValue).hash_code();
NumberOfBytes = sizeof(T);
}
//For strings, do this:
ClassThing(const char* passedValue, unsigned short passedNumberOfBytes)
{
Value = const_cast<char*>(passedValue);
TypeHash = typeid(char*).hash_code();
NumberOfBytes = length;
}
}
Unfortunately, this solution loses the type, but since the serialization and deserialization process I'm using is a simple memcpy, all I needed was a pointer to the data and the number of bytes it used. The reason I store the type's hash here is so that I can perform type checking before serialization (e.g. make sure a float isn't being serialized where an int should be).
For the deserialization process, I will be using this technique: https://stackoverflow.com/a/15313677/1599699
Since I do not know the type, I will simply have to expect that the cast from void* matches up with the serialization process, although I can at least check the NumberOfBytes value and ideally the TypeHash as well, if those are available. On the deserialization end, I will end up with a void* and do this:
void* deserializedData = ...;
float deserializedFloat = *(float*)&deserializedData;
This of course is not the ideal solution to my problem, but it allows me to do what I want, which is extremely high performance serialization and deserialization to binary with low memory usage and extremely low maintenance.
Hope this helps someone!
Although this is not exactly a C++ answer (rather, a C one), it should be valid in C++ all the same.
The type void* is a pointer to untyped memory. Basically, you can cast it to any type of pointer, then dereference. Example:
int x1 = 42;
long l1 = 123456789L;
void* test = &x1;
int x2 = *(int*)test; // x2 now contains the contents of x1
test = &l1;
long l2 = *(long*)test; // l2 now contains the contents of l1
This is in no way the most delicate way of solving your problem, but it is an option.
Further reading:
https://www.astro.umd.edu/~dcr/Courses/ASTR615/intro_C/node15.html
http://www.circuitstoday.com/void-pointers-in-c
http://www.nongnu.org/c-prog-book/online/x658.html
If you want dynamic types (in C++11 or better, e.g. C++14) you could make a variant type by making a class with some union:
class Thing {
enum SupportedTypes type;
union {
intptr_t num; // when type == IntType
double flo; // when type == FloatType
std::string str; // when type == StringType
}
// etc....
};
Be careful, you need to obey to the rule of five and you probably should explicitly call the destructor of std::string on str when type == StringType, etc...
Some third party libraries might be helpful: Boost variants, Qt QVariant, etc...
I'm trying to create an anonymous union that holds a generic vector in order to use it as a member in a class without naming the type_name of the union itself.
So that I could call the vector inside the class as following:
vec.size();
But my approach
template <typename T>
union{
std::vector<T> vec;
};
will only give me the error "template class without a name". This also happens with structures. So does it not like to be anonymous when it is generic?
Search results just gave me the option to create a generic vector inside a named structure but, besides I couldn't get this to work either, I would loose the benefits of the anonymous union und I would need to call the vector e.g. as
struct_name.vec.size();
or even
class_name.struct_name.vec.size();
which I tried to avoid.
I want to make the vector generic so that it can store integers or doubles and I don't need to declare two different vectors with their own specific data types. Beside learning some principles of generics I also aim for lesser declarations and storage usage with this technique.
You cannot reliably do what you want. You need at least some way to discriminate at runtime if you have a vector of int or a vector of float.
With C++11 you might code
class StrangeVector {
bool has_int;
union {
std::vector<int> vint;
std::vector<float> vfloat;
};
public:
StrangeVector(bool withints) : has_int(withints) {
if (withints) new(&vint) std::vector<int>();
else new(&vfloat) std::vector<float>();
}
~StrangeVector() {
if (has_int) vint.~vector<int>();
else vfloat.~vector<float>();
}
};
But such code is really bad smelling. (I would suggest using a union of pointers, perhaps of smart pointers e.g. std::shared_ptr or std::unique_ptr, or perhaps std::optional; see this).
See also this, or boost::any ...
Notice that except for RTTI typing information is used at compile time in C++.
This is my (maybe a little bit weird) thought, suppose I want to define a std::set object to contain some stuff for later use, but unfortunately I'm not sure which type will be passed to std::set<not-sure> as template arg, and this not-sure type will be determined through a string, like this:
class X {
public:
foo()
{
char not_sure_type[20];
scanf("%s", not_sure_type);
if (strcmp(not_sure_type, "int"))
// then std::set<int>
else if (// "char")
// then std::set<char>
}
private:
void * _set;
};
This way, I can determine that std::set<int> will be instantiated or not, right? But how can I tell _set that you should point to a std::set<int>? Without knowing that, either I cannot use static_cast to cast _set from void * to std::set<int>*, back and forth.
So can I save the std::set<int> just like an data member for later use?
Any idea is appreciated.
If you will know the the type of the set element at run-time (based on a say string), you could maybe store a pointer to an abstract type into the set (set), and then use an Abstract Factory in the constructor of the class that holds the std::set to instantiate the actual Concrete Types for the elements during run-time based on the provided string.
The problem is in using raw pointers here, since you will need to do the cleanup within the class that has std::set. Since you want to use std::set, make sure that your Concrete Type for the element is Comparable. Not sure if this is the right way to go though.. you said to throw in ideas...
sounds to me like you are considering using c++ as a weak type language, such as python. sure there could be workarounds like using some abstract base class etc. but the bottom line I think is that defining the type at run time is against the paradigm of c++..
Situation
I have a template class TIppImage<T> for image of type T. I have singleton class CIppMemoryManager which can store a number of images of different size and type.
class CIppMemoryManager
{
public:
/// ... Singleton interface ...
template<class T> TIppImage<T>* GetImage(width, height);
private:
CIppMemoryManager();
~CIppMemoryManager();
std::map<IppDataType, void*> m_Containers;
};
IppDataType is enum, which values correspond to actual types. All management is done in template class TIppImageContainer<T>. And all specialization of this class is stored in m_Containers as a void*. It's not very good, but it is at least simple.
With this approach, I can simply implement template GetImage method like this:
template<class T> TIppImage<T>* CIppMemoryManager::GetImage(width, height)
{
return reinterpret_cast<TIppImageContainer<T>*>(m_Containers[
TIppTypeTraits<T>::ipp_data_type])->GetImage(width, height);
}
where I'm using traits class TIppTypeTraits<T> to obtain enum value from given type.
Problem
I cannot simply implement non-template methods like constructor. I need to explicitly handle all possible types:
CIppMemoryManager::CIppMemoryManager()
{
m_Containers[ipp8u] = new CIppImageContainer<Ipp8u>;
m_Containers[ipp8s] = new CIppImageContainer<Ipp8s>;
m_Containers[ipp16u] = new CIppImageContainer<Ipp16u>;
m_Containers[ipp16s] = new CIppImageContainer<Ipp16s>;
...
}
Worse, for destructor I also need to deal with void*:
CIppMemoryManager::~CIppMemoryManager()
{
delete reinterpret_cast<TIppImageContainer<Ipp8u>*>(m_Containers[ipp8u]);
delete reinterpret_cast<TIppImageContainer<Ipp8s>*>(m_Containers[ipp8s]);
delete reinterpret_cast<TIppImageContainer<Ipp16u>*>(m_Containers[ipp16u]);
delete reinterpret_cast<TIppImageContainer<Ipp16s>*>(m_Containers[ipp16s]);
...
}
So, the questions are:
a) Is there some way to iterate through collection of different types? Cannot use traits class here since function is non-template.
b) Is there some better way to store collection of containers - objects of different type? When they are just a different specialization of common template class, containers itself are pretty simple.
I think the class variant from the boost library (boost::variant) may help you. You can use visitors to execute the appropriate code depending on the type stored in a variant. A std::vector<boost::variant<T0, T1,...>> can store a list of objects of different types.
As your objects are similar, they may have the same size in memory, which is a good thing since boost::variant storage is stack-based (no heap allocation - this is faster).
What's wrong with polymorphic CIppImageContainer<T> (make them all share a common base class) and a smart pointer ?
Or some kind of boost::variant ?
boost::mpl::for_each is tailor-made for that job. Define a vector of types to operate on, a functor or lambda expression to do something, and you are done.
boost::variant is the most likely candidate but sometimes variantS become rather large as they require some extra storage and also have to deal with alignment. So maybe boost::any has advantages in some situations as well:
std::vector<std::pair< Type, boost::any > > data;
To comfortably iterate over such a container is harder (boost::transform_iterator cannot have more than one return type, so this wont work without some template trickery).