I am attempting to figure out how to achieve runtime polymorphism with a class called Signal, where its data type is passed as enum value to the constructor. Compile-time polymorphism with templates is not suitable.
As all values in the "signal" are supposed to be of same type, I chose variant-of-vectors instead of vector-of-variants.
enum class Type { UINT8, INT16, FLOAT };
using VariantVector = std::variant<std::vector<uint8_t>, std::vector<int16_t>, std::vector<float>>;
class Signal {
public:
explicit Signal(size_t size, Type type, double value)
: m_size(size), m_type(type) {
// Create variant vector of said type and initialize it with value
}
// Various methods ...
private:
size_t m_size;
Type m_type;
VariantVector m_data; // Variant of vectors
};
I have been able to make most of the things work, but got stuck on how to override the iterator methods. In order to use range-based loops as
Signal signal(100, Type::UINT8, 10);
for(auto& item : signal){
// Do stuff with item
}
the begin() and end() functions need to be defined. I managed to partially solve this with explicit template functions as
template <typename T>
std::vector<T>::iterator begin();
template <typename T>
std::vector<T>::const_iterator begin() const;
template <typename T>
std::vector<T>::iterator end();
template <typename T>
std::vector<T>::const_iterator end() const;
but this does not work for range-based loops (and I'd like to avoid explicit templates anyway).
I tried to implement a couple solutions with std::visit, but did not get far - most solutions were based on operator overloading. Also looked into the double-dispatch visitor pattern, but don't really see how to apply it here.
How to achieve this with a variant of vectors?
Is this even possible or am I attempting to bend some rules of C++ wizardry?
Any insight is more than welcome. Thanks.
EDIT #1:
#Wyck asked in the comments how I'd like range-based loop to work. Ideally I'd like a situation as:
{
Signal signal(100, Type::UINT8, 10);
for(auto& item : signal){
// item is of type uint8&
}
}
{
Signal signal(100, Type::FLOAT, 10);
for(auto& item : signal){
// item is of type float&
}
}
Note the reference to the type, which means changing it should modify the private variant contents of Signal as well.
Related
I'm writing an adaptor class to allow me to write this:
for (auto item : ButFirst(myvec)) { ... }
The following class definition works well when, as above, the argument is an lval:
template <typename Container>
class ButFirst {
const Container& container_;
public:
ButFirst(const Container& container) : container_(container) {}
typename Container::const_iterator begin() { return ++(container_.begin()); }
typename Container::const_iterator end() { return container_.end(); }
};
However, I want to also use it when the argument is an rval, as in:
for (auto item : ButFirst(get_vec(3)) { ... }
I would therefore write the following class definition:
template <typename Container>
class ButFirst {
const Container container_;
public:
ButFirst(Container&& container) : container_(std::move(container)) {}
...
};
How can I write one class definition that handles both? Or maybe one class and some partial template specializations?
template <typename Container>
class ButFirst {
Container container_;
public:
ButFirst(Container container) : container_(std::forward<Container>(container)) {}
typename std::decay_t<Container>::const_iterator begin() { return std::next(container_.begin()); }
typename std::decay_t<Container>::const_iterator end() { return container_.end(); }
};
then add deduction guide:
template <typename Container>
ButFirst(Container&&)->ButFirst<Container>;
and ... poof.
Despite appearances this only copies on rvalues.
The forwarding reference in the ButFirst deduction guide deduces Container to be a reference type when you construct it with an lvalue, and Container as a value type when you pass it an rvalue. This is exactly what you want.
And that isn't a coincidence; forwarding references work that way for a reason.
live example.
As an aside, my version of this is a bit different.
I define a range_view type that can be constructed from a range-like, and stores 2 iterators.
range_view methods like begin() are const; range_view is like a pointer, not like a value.
It has methods:
range_view except_front(std::size_t n=1)const;
range_view except_back(std::size_t n=1)const;
range_view only_front(std::size_t n=1)const;
range_view only_back(std::size_t n=1)const;
which clamp n based on how big the range is.
Then:
for(auto item: range_view(container).except_front())
does what yours does.
I consider checking for iterator counting errors and returning empty range in that case well worth the overhead for the reliability I get from it, as that math happens once per loop, not once per iteration.
Also, range_view has ridiculously many other useful uses.
I have use case similar to this question
I want to check what type of instance variable is stored in parameter without throwing an exception
class ParameterBase
{
public:
virtual ~ParameterBase() {}
template<class T> const T& get() const; //to be implimented after Parameter
template<class T, class U> void setValue(const U& rhs); //to be implimented after Parameter
};
template <typename T>
class Parameter : public ParameterBase
{
public:
Parameter(const T& rhs) :value(rhs) {}
const T& get() const {return value;}
void setValue(const T& rhs) {value=rhs;}
private:
T value;
};
//Here's the trick: dynamic_cast rather than virtual
template<class T> const T& ParameterBase::get() const
{ return dynamic_cast<const Parameter<T>&>(*this).get(); }
template<class T, class U> void ParameterBase::setValue(const U& rhs)
{ return dynamic_cast<Parameter<T>&>(*this).setValue(rhs); }
class Diagram
{
public:
ParameterBase* v;
int type;
};
What I want to be able to do is something like this
if (diagram.getParameter().type == int) {
}
How can I change this implementation so it will allow me to peek what type of Parameter is holding
Thanks for the answers , few more points
I am on C++ 11 so cannot use variant or any
Is there standard way of doing this. All I want is an instance variable of class that can be of multiple types (bounded) and while reading it check what type it is
The Simple Fix
The simple solution to your problem is to add a template function is<T>() to your ParameterBase that is defined in terms of dynamic_cast on a pointer. dynamic_cast with pointers return nullptr on failure, unlike references which will throw a std::bad_cast. For example:
class ParameterBase
{
public:
...
template <typename T>
bool is() const;
};
...
template <typename T>
bool ParameterBase::is() const
{
return dynamic_cast<const Parameter<T>*>(this) != nullptr;
}
The use would be simply:
if (diagram.getParameter().is<int>()) {
...
}
Note, however, that this whole design is not particularly nice. It has a cyclic dependency between the base and derived in a way that is highly coupled. Additionally it requires ParameterBase to exist as a pointer in order to operate correctly; where value-semantics would be much more coherent (if possible)
It would be better if you can use type-erasure, even if you define Parameter in terms of it (this is what C++17's std::any will do for you). The second answer in your linked question already describes what this may look like.
Type-erased Solution (c++11)
This uses C++11 features like forwarding references, rvalue-references, and unique_ptr -- but the concept can also be applied to earlier C++ versions.
For type-erasure, you would need an interface that encompasses at least these 2 features:
getting a reference to the templated type, and
getting an identifier for the current type.
Since interfaces in C++ can't be virtual, we have to get creative about returning the reference. C++ has void* which can be any kind of pointer. This can be bad if misused (such as casting between the wrong type); but if we know the underlying type, can be perfect. Thankfully here, we know the underlying type.
A quick form of type-erasure could be achieved with the following:
#include <type_traits> // std::decay
#include <utility> // std::forward
#include <typeinfo> // std::type_info, std::bad_cast
#include <memory> // std::unique_ptr
class Parameter
{
private:
// This is the interface we will implement in all instances
struct Interface {
virtual ~Interface() = default;
virtual void* get() = 0;
virtual const std::type_info& type() const = 0;
};
// This is the concrete instantiation of the above interfaces
template <typename T>
struct Concrete : public Interface {
template <typename U>
Concrete(U&& u) : m_value{std::forward<U>(u)} {}
void* get() { return &m_value; }
const std::type_info& type() const { return typeid(T); }
T m_value; // actually holds the value here
};
// This holds onto the interface, and only the interface
std::unique_ptr<Interface> m_interface;
public:
// Constructs a parameter and sets the first interface value
template <typename T>
explicit Parameter(T&& value)
: m_interface{new Concrete<typename std::decay<T>::type>{std::forward<T>(value)}}
{}
Parameter(Parameter&&) = default;
Parameter& operator=(Parameter&&) = default;
// Check if we are the same type by comparing the typeid
template <typename T>
bool is() const {
return typeid(T) == m_interface->type();
}
// Get the underlying value. Always check that we are the correct type first!
template <typename T>
const T& get() const {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
// cast void* to the underlying T*. We know this is safe
// because of our check above first
return (*static_cast<T*>(m_interface->get()));
}
// Set the underlying value. Always check that we are the correct type first!
template <typename T, typename U>
void set(U&& value) {
// do the error handling ourselves
if (!is<T>()) { throw std::bad_cast{}; }
(*static_cast<T*>(m_interface->get())) = std::forward<U>(value);
}
};
In the above, we take on the burden of detecting the underlying type ourselves -- but we remove the cyclic coupling. We now also have a proper value-type that we can move around like a normal variable, which is really helpful since it allows us to return this object from APIs without worrying about lifetime or ownership.
If copyability is desired as well, the interface can be extended to have a clone() function or something to return copies
Using this object, the code becomes:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
Here's a small working example.
Type-erased Solution (c++17)
If you're looking for a finite set of instantiations, std::variant may be used for this purpose. If the number of possibly underlying types is unbounded, you should look into std::any
In either case, the use of a hierarchy here is superficial (at least in the current example) since the entire type-erasure can be reduced to a singular type with the ability to query the containment. This could be done easily, using std::any as an example:
#include <any> // std::any, std::any_cast
class Parameter
{
public:
// This implementation changes the active type if 'T' is not the same as the stored
// value. If you want to restrict this, you can do error checking here instead.
template <typename T>
void set(const T& value) { m_value = value; }
template <typename T>
const T& get() { return std::any_cast<const T&>(m_value); }
template <typename T>
bool is() const noexcept { return m_value.type() == typeid(T); }
private:
std::any m_value;
};
If you don't want the active member to change, this could be restricted by checking is<T>() first and handling the error somehow.
Querying the active type can be achieved simply by doing:
if (parameter.is<int>()) {
/* treat parameter as an int */
}
If the types are fixed, you can always use std::variant instead using std::has_alternative for the definition of is
Looks like you know in advance all possible types for the parameter (I'm saying that because you have a type field that is expected to be used as an enumeration). If that is the case, you may use the std::variant idiom:
class Diagram
{
public:
std::variant<Parameter<int>, Parameter<std::string>> v;
};
In this case you may use this code to get known the actual type:
switch(v.index()) {
case 0:
// int is used
case 1:
// string is used
}
For sure there are other alternatives. For example, if you have something of a type and you need to test if that is the type expect, you my use std::is_same template:
template <typename T>
class Parameter : public ParameterBase
{
public:
bool isOfTypeInt() const {
return std::is_same_v<T, int>;
}
private:
T value;
};
So, I found it more natural to work in algorithms with collections, not with pair of iterators. So, I wrote some functions like
template <typename R>
void sort(R& range) {
return std::sort(std::begin(range), std::end(range));
}
And to have possibility to work with parts of collections I've wrote following wrapper class, that just holds pair of iterators.
template <typename T>
class Range{
public:
Range(T begin, T end): begin_(begin), end_(end) {}
const T& begin() {
return begin_;
}
const T& end() {
return end_;
}
private:
T begin_, end_;
};
To that point all is fine. Now I want to have function that copies/(moves if possible) its argument and return new collection.
I wrote something like this:
template <typename R>
R sorted(R range) {
sort(range);
return std::move(range);
}
and that's fine except that if I call it with my wrapper Range class internal collection changed. I do understand that with only iterator type it's generally impossible to retrieve the type of collection to create new one, but I want to at least disallow calling it with this Wrapper.
I do understand that I can use static_assert of enable_if to check if it is of particular Range class and I will do that way unless I will find better way. But I want ban it somehow in more general way, so that similar implementation will fail to compile too.
Any ideas?
You can delete the function like:
template <typename T>
void sorted(const Range<T>& range) = delete;
else you can disallow copy and move of the object, so it can only be used with reference
template <typename T>
class Range{
public:
Range(const Range&) = delete;
Range(Range&&) = delete;
Range& operator =(const Range&) = delete;
Range& operator =(Range&&) = delete;
// previous code
};
I would create a traits class called owning_container. By default it considers arguments that are ranges (you should have a traits class/concept constexpr for that -- if begin(x) in a namespace with using std::begin; returns an iterator, call it a range) and have an allocator (another trait) to be owning (as non-owning ranges usually have no need for an allocator) as well as C arrays and std::arrays as owning (via specialization).
This also allows me to detect rvalue owning containers and move their contents in certain contexts (change their iterators into move iterators) without doing the same to non-owning range views.
As mentioned above, a constexpr pseudo-concept might be better than a traits class, or might be useful to augment it.
I have a templated class that has a data member of type std::vector<T>, where T is also a parameter of my templated class.
In my template class I have quite some logic that does this:
T &value = m_vector[index];
This doesn't seem to compile when T is a boolean, because the [] operator of std::vector does not return a bool-reference, but a different type.
Some alternatives (although I don't like any of them):
tell my users that they must not use bool as template parameter
have a specialization of my class for bool (but this requires some code duplication)
Isn't there a way to tell std::vector not to specialize for bool?
You simply cannot have templated code behave regularly for T equal to bool if your data is represented by std::vector<bool> because this is not a container. As pointed out by #Mark Ransom, you could use std::vector<char> instead, e.g. through a trait like this
template<typename T> struct vector_trait { typedef std::vector<T> type; };
template<> struct vector_trait<bool> { typedef std::vector<char> type; };
and then use typename vector_trait<T>::type wherever you currently use std::vector<T>. The disadvantage here is that you need to use casts to convert from char to bool.
An alternative as suggested in your own answer is to write a wrapper with implicit conversion and constructor
template<typename T>
class wrapper
{
public:
wrapper() : value_(T()) {}
/* explicit */ wrapper(T const& t): value_(t) {}
/* explicit */ operator T() { return value_; }
private:
T value_;
};
and use std::vector< wrapper<bool> > everywhere without ever having to cast. However, there are also disadvantages to this because standard conversion sequences containing real bool parameters behave differently than the user-defined conversions with wrapper<bool> (the compiler can at most use 1 user-defined conversion, and as many standard conversions as necessary). This means that template code with function overloading can subtly break. You could uncomment the explicit keywords in the code above but that introduces the verbosity again.
Use std::vector<char> instead.
Would the following work for you?
template <typename T>
struct anything_but_bool {
typedef T type;
};
template <>
struct anything_but_bool<bool> {
typedef char type;
};
template <typename T>
class your_class {
std::vector<typename anything_but_bool<T>::type> member;
};
Less flippantly, the name anything_but_bool should probably be prevent_bool or similar.
I found an even more elegant solution, based on all of your input.
First I define a simple class that holds one member. Let's call this wrapperClass:
template <typename T>
class wrapperClass
{
public:
wrapperClass() {}
wrapperClass(const T&value) : m_value(value) {}
T m_value;
};
Now I can define my std::vector in my templated class like this:
std::vector<wrapperClass<T>> m_internalVector;
Since sizeof(WrapperClass<bool>) is also 1, I expect that sizeof(WrapperClass<T>) will always be equal to sizeof(T). Since the data type is now not a bool anymore, the specialization is not performed.
In places where I now get an element from the vector, I simply replace
m_internalVector[index]
by
m_internalVector[index].m_value
But this seems much more elegant than using traits to replace bool by char, and then using casts to convert between char and bool (and probably reinterpret casts to convert char& to bool&).
What do you think?
You could use a custom proxy class to hold the bools.
class Bool
{
public:
Bool() = default;
Bool(bool in) : value(in) {}
Bool& operator=(bool in) {value = in;}
operator bool() const& {return value;}
private:
bool value;
};
This might require a bit of tweaking for your purposes, but it's usually what I do in these cases.
std::basic_string<bool> flags(flagCount, false);
Semantically, using string is weird, but it works basically like std::vector, behaves as expected with pointers/references, and it conveniently converts to std::span<bool> when passing to functions that take span, no need for wrapper classes or reinterpret_cast's (e.g. using std::vector<uint8_t> makes these cases awkward because of the casts). Until/unless C++ adds a std::bit_vector and deprecates the specialization, we don't have many good options.
There is a ways to prevent the vector<bool> specialization: Passing a custom allocator.
std::vector<bool, myallocator> realbool;
The following article has some details:
https://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=98
Of course, this requires that you have control over the vector definitions, so it's probably not really a solution for you. Apart from that, it also has some downsides of it's own...
I have a C++ class Finder that stores a location in containers plus some additional data. Such containers have the type std::string but also char * or MyString. Say, the class looks like this (Iterator<TContainer>::Type is a trait / metafunction that returns the iterator type for a given container):
template <typename TContainer>
class Finder
{
public:
typedef typename Iterator<TContainer>::Type TIterator;
TIterator iterator;
// Initialization of iterator: How to do it generically?
// Finder {} // problem with pointers!
// Finder() : iterator(0) {} // problem with iterators!
};
The main problem now is how to initialize the iterator that can be a pointer or an iterator. If I only wanted to support my own containers then I could simply implement a constructor that takes nullptr_t following the nullptr idiom. However, since I want to support pointers, my own iterators and STL iterators, I'm a bit out of depth here.
The best thing that I can imagine is to write a getNil<>() function, e.g. the code below. However, now at least three questions arise:
Is this the best way to achieve my aim?
How to determine whether a type is a STL iterator? I would probably need some #ifs, and tailor code to each compiler/STL version to use.
Is it possible at all to get a nil iterator in the STL? Is the result of x-y in std::vector<int> x, y; int x = x-y; defined at all?
The code:
// Forward declaration.
template <typename T>
T getNil<T>();
template <typename TValue> TValue * getNil<TValue *>()
{ return NULL; }
template <typename TValue> TValue * const getNil<TValue * const>()
{ return NULL; }
template <> TValue * const getNil<MyIterator>()
{ return MyIterator(nullptr); } // nullptr from above idiom
template <> TStlIterator
boost::enable_if<
MagicallyDetermineIfIsStlIterator<TStlIterator>::value,
TStlIterator>
getNil<TStlIterator>()
{ return MyIterator(nullptr); } // nullptr from above idiom
Finder() : iterator() { }
Should do the trick. Not providing an argument for a member in the initialization list will call the default constructor on a type that has one, and will zero-initialize POD types including pointers (and it will fail if the type has no default constructor, but that seems unlikely given your scenario).