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.
Related
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.
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.
When implementing a custom container I came to the point where I needed to implement iterators. Of course I didn't want to write the code twice for const and non-const iterator. I found this question detailing a possible implementation like this:
template<class T>
class ContainerIterator {
using pointer = T*;
using reference = T&;
...
};
template<class T>
class Container {
using iterator_type = ContainerIterator<T>;
using const_iterator_type = ContainerIterator<const T>;
}
But I also found this this question that uses a template parameter:
template<class T, bool IsConst>
class ContainerIterator {
using pointer = std::conditional_t<IsConst, const T*, T*>;
using reference = std::conditional_t<IsConst, const T&, T&>;
...
};
template<class T>
class Container {
using iterator_type = ContainerIterator<T, false>;
using const_iterator_type = ContainerIterator<T, true>;
}
The first solution seems to be easier, but the answer is from 2010.
After doing some research, it seems, that the first version is not widely used, but I can't see why. I feel like I'm missing some obvious flaw of the first version.
So the questions become:
Are there any problems with the first version?
If no, why version #2 seems to be the preferred way in c++17? Or why should I prefer one over the other?
Also, yes, it would be a solution to use const_cast or simply duplicate the whole code. But I don't like those two.
The point of the second implementation is something you didn't copy: to make it easy to implement a specific requirement. Namely, an iterator must be implicitly convertible to a const_iterator.
The difficulty here lies in preserving trivial copyability. If you were to do this:
template<class T>
class ContainerIterator {
using pointer = T*;
using reference = T&;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
ContainerIterator(const ContainerIterator<const T> &) {...}
};
That doesn't work. const const Typename resolves down to const Typename. So if you instantiate ContainerIterator with a const T, then you now have two constructors that have the same signature, one of which is defaulted. Well, this means that the compiler will ignore your defaulting of the copy constructor and thus use you non-trivial copy constructor implementation.
That's bad.
There are ways to avoid this by using some metaprogramming tools to detect the const-ness of the T, but the easiest way to fix it is to specify the const-ness as a template parameter:
template<class T, bool IsConst>
class ContainerIterator {
using pointer = std::conditional_t<IsConst, const T*, T*>;
using reference = std::conditional_t<IsConst, const T&, T&>;
...
ContainerIterator(const ContainerIterator &) = default; //Should be trivially copyable.
template<bool was_const = IsConst, class = std::enable_if_t<IsConst || !was_const>>>
ContainerIterator(const ContainerIterator<T, was_const> &) {...}
};
Templates are never considered to be copy constructors, so this won't interfere with trivial copyability. This also uses SFINAE to eliminate the converting constructor in the event that it is not a const iterator.
More information about this pattern can be found as well.
I am interested in implementing a Java-collection-like environment for C++.
I know this isn't a good idea and so on but I don't really want to use it later, but just learn how to do some advanced OOP.
My problem is I want a base class template collection<T> with purely virtual functions. One of these functions should be map() which takes a std::function<R(T)>. Since map() should be virtual I don't know which return type I should use for it. collection<R> isn't possible because member function templates can't be virtual.
How can I add such map() member function for my collection<T> interface?
How can I add such map member function for my collection<T> interface?
The short answer is: you don't. If I have some collection<int> and I want to map std::to_string onto it, I need to produce a collection<std::string>. But a vector_collection<int> needs to produce a vector_collection<std::string> and a list_collection<int> needs to produce a list_collection<std::string> - so that type transformation itself needs to be virtualized. But you can't have virtual member function templates, so there's no way to express this.
In order for this to work, you would have to have a common base type for all of the objects you're putting in your container and then just have a common facade that you could cast between. That is, you really only have collection<unique_ptr<Object>> where map just gives you a different collection<unique_ptr<Object>>, and you just map your collection_facade<int, collection<unique_ptr<Object>>> into a collection_facade<std::string, collection<unique_ptr<Object>>>. With a lot of work and complete disregard for performance and type safety, you could get there.
This is the advantage of templates. If I want to write map for something like vector, I can just write that:
template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
std::vector<R, A> mapped;
mapped.reserve(v.size());
for (T const& elem : v) {
mapped.push_back(f(elem));
}
return mapped;
}
or:
template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
return std::vector<R, A>(
boost::make_transform_iterator(v.begin(), f),
boost::make_transform_iterator(v.end(), f)
);
}
I have to implement map() for each container separately - but I would have to do that anyway. And now I'm not giving anything up. Besides, how often are you writing algorithms that are runtime-container-agnostic?
Implement map as an external template function.
For instance, you can decompose map in two stages, internal virtual producer and external templated consumer.
template<typename T> struct Collection {
// virtual T next(); // Java way
// C++ way
// In simplest cases you can rely on iterator pairs.
struct const_iterator {
T const &operator*() const;
const_iterator &operator++();
}
virtual const_iterator begin() const;
virtual const_iterator end() const;
};
template<typename R, typename T> Collection<R> map(
Collection<T> const &coll, std::function<R(T)> const &f);
To implement essentially complicated containers and monadic compositions you can even deny begin() and end() and write an explicit (partial) template specialization.
I'd like to specialize std::iterator_traits<> for iterators of a container class template that does not have the usual nested typedefs (like value_type, difference_type, etc.) and whose source I shouldn't modify. Basically I'd like to do something like this:
template <typename T> struct iterator_traits<typename Container<T>::iterator>
{
typedef T value_type;
// etc.
};
except that this doesn't work, as the compiler is unable to deduce T from Container<T>::iterator.
Is there any working way to achieve the same?
For example:
template <typename T>
class SomeContainerFromAThirdPartyLib
{
typedef T ValueType; // not value_type!
// no difference_type
class iterator
{
typedef T ValueType; // not value_type!
// no difference_type
...
};
iterator begin() { ... }
iterator end() { ... }
...
};
Now suppose I call std::count() using an instance of this class. As far as I know, in most STL implementations, count() returns iterator_traits<Iterator>::difference_type. The primary template of iterator_traits<I> simply does typedef typename I::difference_type difference_type. Same with the other nested types.
Now in our example this obviously won't work, as there's no Container::iterator::difference_type. I thought I could work around this without modifying the iterator class, by specializing iterator_traits for iterators of any Container<T>.
In the end, I just want to be able to use std algorithms like count, find, sort, etc., preferably without modifying any existing code. I thought that the whole point of iterator_traits is exactly that: being able to specify types (like value_type, diff_type etc.) for iterator types that do not support them built-in. Unfortunately I can't figure out how to specialize the traits class for all instances of Container<T>.
Yes. The compiler cannot deduce T from Container<T>::iterator because it is non-deducible context, which in other words means, given Container<T>::iterator, the value of T cannot uniquely and reliably be deduced (see this for detail explanation).
The only solution to this problem is that you've to fully specialize iterator_traits for each possible value of iterator which you intend to use in your program. There is no generic solution, as you're not allowed to edit the Container<T> class template.
Nawaz's answer is likely the right solution for most cases. However, if you're trying to do this for many instantiated SomeContainerFromAThirdPartyLib<T> classes and only a few functions (or an unknown number of instantiations but a fixed number of functions, as might happen if you're writing your own library), there's another way.
Assume we're given the following (unchangeable) code:
namespace ThirdPartyLib
{
template <typename T>
class SomeContainerFromAThirdPartyLib
{
public:
typedef T ValueType; // not value_type!
// no difference_type
class iterator
{
public:
typedef T ValueType; // not value_type!
// no difference_type
// obviously this is not how these would actually be implemented
int operator != (const iterator& rhs) { return 0; }
iterator& operator ++ () { return *this; }
T operator * () { return T(); }
};
// obviously this is not how these would actually be implemented
iterator begin() { return iterator(); }
iterator end() { return iterator(); }
};
}
We define an adapter class template containing the necessary typedefs for iterator_traits and specialize it to avoid problems with pointers:
namespace MyLib
{
template <typename T>
class iterator_adapter : public T
{
public:
// replace the following with the appropriate types for the third party iterator
typedef typename T::ValueType value_type;
typedef std::ptrdiff_t difference_type;
typedef typename T::ValueType* pointer;
typedef typename T::ValueType& reference;
typedef std::input_iterator_tag iterator_category;
explicit iterator_adapter(T t) : T(t) {}
};
template <typename T>
class iterator_adapter<T*>
{
};
}
Then, for each function we want to be able to call with a SomeContainerFromAThirdPartyLib::iterator, we define an overload and use SFINAE:
template <typename iter>
typename MyLib::iterator_adapter<iter>::difference_type
count(iter begin, iter end, const typename iter::ValueType& val)
{
cout << "[in adapter version of count]";
return std::count(MyLib::iterator_adapter<iter>(begin), MyLib::iterator_adapter<iter>(end), val);
}
We can then use it as follows:
int main()
{
char a[] = "Hello, world";
cout << "a=" << a << endl;
cout << "count(a, a + sizeof(a), 'l')=" << count(a, a + sizeof(a), 'l') << endl;
ThirdPartyLib::SomeContainerFromAThirdPartyLib<int> container;
cout << "count(container.begin(), container.end(), 0)=";
cout << count(container.begin(), container.end(), 0) << std;
return 0;
}
You can find a runnable example with the required includes and usings at http://ideone.com/gJyGxU. The output:
a=Hello, world
count(a, a + sizeof(a), 'l')=3
count(container.begin(), container.end(), 0)=[in adapter version of count]0
Unfortunately, there are caveats:
As I said, an overload needs to be defined for each function you plan to support (find, sort, et cetera). This obviously won't work for functions in algorithm that haven't been defined yet.
If not optimized out, there may be small run-time performance penalties.
There are potential scoping issues.
In regards to that last one, the question is in which namespace to put the overload (and how to call the std version). Ideally, it would be in ThirdPartyLib so that it could be found by argument-dependant lookup, but I've assumed we can't change that. The next best option is in MyLib, but then the call has to be qualified or preceded by a using. In either case the end-user should either use using std::count; or be careful about which calls to qualify with std::, since if std::count is mistakenly used with SomeContainerFromAThirdPartyLib::iterator, it will obviously fail (the whole reason for this exercise).
An alternative that I do not suggest but present here for completeness would be to put it directly in the std namespace. This would cause undefined behavior; while it might work for you, there's nothing in the standard that guarantees it. If we were specializing count instead of overloading it, this would be legal.
In the specialization in question, T is in a nondeducible context but there is neither a third party library container code change nor any specialization in the std namespace required.
If the third party library does not provide any free begin and end functions in the respective namespace one can write own functions (into that namespace if desired to enable ADL) and wrap the iterator into an own wrapper class which in turn provides the necessary typedefs and operators.
First one needs the Iterator wrapper.
#include <cstddef>
namespace ThirdPartyStdAdaptor
{
template<class Iterator>
struct iterator_wrapper
{
Iterator m_it;
iterator_wrapper(Iterator it = Iterator())
: m_it(it) { }
// Typedefs, Operators etc.
// i.e.
using value_type = typename Iterator::ValueType;
using difference_type = std::ptrdiff_t;
difference_type operator- (iterator_wrapper const &rhs) const
{
return m_it - rhs.m_it;
}
};
}
Note: It would also be possible to make iterator_wrapper inherit from Iterator, or to make it more generic and have another helper to enable the wrapping of other iterators as well.
Now begin() and end():
namespace ThirdPartyLib
{
template<class T>
ThirdPartyStdAdaptor::iterator_wrapper<typename
SomeContainer<T>::iterator> begin(SomeContainer<T> &c)
{
return ThirdPartyStdAdaptor::iterator_wrapper<typename
SomeContainer<T>::iterator>(c.begin());
}
template<class T>
ThirdPartyStdAdaptor::iterator_wrapper < typename
SomeContainer<T>::iterator > end(SomeContainer<T> &c)
{
return ThirdPartyStdAdaptor::iterator_wrapper < typename
SomeContainer<T>::iterator > (c.end());
}
}
(It is also possible to have them in a different namespace than SomeContainer but loose ADL. IF there are begin and end functions present in the namespace for that container I'd tend to rename the adaptors to be something like wbegin and wend.)
The standard algorithms can be called using those functions now:
ThirdPartyLib::SomeContainer<SomeType> test;
std::ptrdiff_t d = std::distance(begin(test), end(test));
If begin() and end() are included into the library namespace, the container can even be used in more generic contexts.
template<class T>
std::ptrdiff_t generic_range_size(T const &x)
{
using std::begin;
using std::end;
return std::distance(begin(x), end(x));
}
Such code can be used with std::vector as well as ThirdPartyLib::SomeContainer, as long as ADL finds begin() and end() returning the wrapper iterator.
You can very well use the Container as template parameter to your iterator_traits. What matters to the rest of STL are the typedefs inside your traits class, such as value_type. Those should be set correctly:
template <class Container> struct iterator_traits
{
public:
typedef typename Container::value_type value_type;
// etc.
};
You would then use value_type where you would previously use T.
As for using the traits class, you of course parametrize it with the type of your external container:
iterator_traits<TheContainer> traits;
Naturally, this assumes TheContainer is conforms to the common STL containers' contract and has value_type defined correctly.