Suppose I write template function that processes a range of elements.
template <typename Iter>
void func(Iter first, Iter last);
In this function I want to call some low-level c-function that expects to get contiguous buffer and it's size. Generic way to do this stuff is to copy my range to contiguous container and then call c-function.
template <typename Iter>
void func(Iter first, Iter last)
{
typedef typename iterator_traits<Iter>::value_type value_type;
vector<value_type> buf(first, last);
c_func((void*) buf.data(), buf.size() * sizeof(value_type));
}
But if iterators are already point to some contiguous memory space extra copying will be performed.
So the question is, is there a way to determine are iterators point to contiguous memory space and if it is how can I specialize my function for this case.
At the moment there is no direct way to determine if an iterator is used for contiguous memory. There is a proposal to add an iterator refinement (see e.g. n3884) but without something like that and a way to detect the property being added for the different iterators you'll have a hard time determining this trait.
The key problem requiring implementer support is that the name if iterator types is not specified. As a result, you can't create a traits applicable to all known to be contiguous iterator types. Since the iterator types for std::vector<T> and std::array<T> can be identical and T* I don't think you can even create a portable specialization.
What you could do, however, is to test in a trait whether an iterator type matches one of the known iterator types for its value type. For example:
template <typename T, typename It>
struct test_iterator
: std::integral_constant<bool,
std::is_same<T*, It>::value
|| std::is_same<typename std::vector<T>::iterator, It>::value
|| std::is_same<std::string::iterator, It>::value
|| std::is_same<std::wstring::iterator, It>::value
> {
};
template <typename It>
struct is_contiguous
: std::integral_constant<bool,
test_iterator<typename std::iterator_traits<It>::value_type,
It>::value> {
};
[I haven't tried to compile the code, i.e., it is probably littered with small typos; the general approach should work, though]
To add, e.g., std::array<T, N> you'd need some way to figure out the dimension in a static way. I guess, that won't be viable. It may also be necessary to test the various const_iterators.
Related
I have written a fancy "zip iterator" that already fulfils many roles (can be used in for_each, copy loops, container iterator range constructors etc...).
Under all the template code to work around the pairs/tuples involved, it comes down to the dereference operator of the iterator returning a tuple/pair of references and not a reference to a tuple/pair.
I want my iterator to work with std::sort, so I need to be able to do swap(*iter1, *iter2) and have the underlying values switched in the original containers being iterated over.
The code and a small demo can be viewed here (it's quite a bit to get through): http://coliru.stacked-crooked.com/a/4fe23b4458d2e692
Although libstdc++'s sort uses std::iter_swap which calls swap, e.g. libc++'s does not, and it just calls swap directly, so I would like a solution involving swap as the customization point.
What I have tried (and gotten oooooh so close to working) is instead of returning std::pair/std::tuple from the operator* as I am doing now, is returning a simple wrapper type instead. The intent is to have the wrapper behave as if it were a std::pair/std::tuple, and allow me to write a swap function for it.
It looked like this:
template<typename... ValueTypes>
struct TupleWrapper : public PairOrTuple_t<ValueTypes...>
{
using PairOrTuple_t<ValueTypes...>::operator=;
template<typename... TupleValueTypes>
operator PairOrTuple_t<TupleValueTypes...>() const
{
return static_cast<PairOrTuple_t<ValueTypes...>>(*this);
}
};
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>& tupleWrapper)
{
return std::get<Index>(tupleWrapper);
}
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>&& tupleWrapper)
{
return std::get<Index>(std::forward<TupleWrapper<ValueTypes...>>(tupleWrapper));
}
template<typename... ValueTypes,
std::size_t... Indices>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right,
const std::index_sequence<Indices...>&)
{
(std::swap(std::get<Indices>(left), std::get<Indices>(right)), ...);
}
template<typename... ValueTypes>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right)
{
swap(left, right, std::make_index_sequence<sizeof...(ValueTypes)>());
}
namespace std
{
template<typename... ValueTypes>
class tuple_size<utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_size<utility::implementation::PairOrTuple_t<ValueTypes...>> {};
template<std::size_t Index, typename... ValueTypes>
class tuple_element<Index, utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_element<Index, utility::implementation::PairOrTuple_t<ValueTypes...>> {};
}
Full code here: http://coliru.stacked-crooked.com/a/951cd639d95af130.
Returning this wrapper in operator* seems to compile (at least on GCC) but produces garbage.
On Clang's libc++, the std::tie fails to compile.
Two questions:
How can I get this to compile with libc++ (the magic seems to lie in the conversion operator of TupleWrapper?)
Why is the result wrong and what did I do wrong?
I know it's a lot of code, but well, I can't get it any shorter as all the tiny examples of swapping tuple wrappers worked fine for me.
1st problem
One of the issues is that the ZipIterator class does not satisfy the requirements of RandomAccessIterator.
std::sort requires RandomAccessIterators as its parameters
RandomAccessIterators must be BidirectionalIterators
BidirectionalIterators must be ForwardIterators
ForwardIterators have the condition that ::reference must be value_type& / const value_type&:
The type std::iterator_traits<It>::reference must be exactly
T& if It satisfies OutputIterator (It is mutable)
const T& otherwise (It is constant)
(where T is the type denoted by std::iterator_traits<It>::value_type)
which ZipIterator currently doesn't implement.
It works fine with std::for_each and similar functions that only require the iterator to satisfy the requirements of InputIterator / OutputIterator.
The reference type for an input iterator that is not also a LegacyForwardIterator does not have to be a reference type: dereferencing an input iterator may return a proxy object or value_type itself by value (as in the case of std::istreambuf_iterator).
tl;dr: ZipIterator can be used as an InputIterator / OutputIterator, but not as a ForwardIterator, which std::sort requires.
2nd problem
As #T.C. pointed out in their comment std::sort is allowed to move values out of the container and then later move them back in.
The type of dereferenced RandomIt must meet the requirements of MoveAssignable and MoveConstructible.
which ZipIterator currently can't handle (it never copies / moves the referenced objects), so something like this doesn't work as expected:
std::vector<std::string> vector_of_strings{"one", "two", "three", "four"};
std::vector<int> vector_of_ints{1, 2, 3, 4};
auto first = zipBegin(vector_of_strings, vector_of_ints);
auto second = first + 1;
// swap two values via a temporary
auto temp = std::move(*first);
*first = std::move(*second);
*second = std::move(temp);
// Result:
/*
two, 2
two, 2
three, 3
four, 4
*/
(test on Godbolt)
Result
Unfortunately it is not possible to create an iterator that produces elements on the fly and can by used as a ForwardIterator with the current standard (for example this question)
You could of course write your own algorithms that only require InputIterators / OutputIterators (or handle your ZipIterator differently)
For example a simple bubble sort: (Godbolt)
template<class It>
void bubble_sort(It begin, It end) {
using std::swap;
int n = std::distance(begin, end);
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (*(begin+j) > *(begin+j+1))
swap(*(begin+j), *(begin+j+1));
}
}
}
Or change the ZipIterator class to satisfy RandomAccessIterator.
I unfortunately can't think of a way that would be possible without putting the tuples into a dynamically allocated structure like an array (which you're probably trying to avoid)
I want to determine the std container type (at least associative or linear) from an iterator passed to a function.
I have searched for appropriate iterator traits and type traits without success.
template <typename RangeIter, typename InputIter>
inline bool filter(RangeIter in_data, InputIter result)
{
...
/* determine the container types here - but how?!? */
std::copy_if(in_data.first, in_data.second, result, /* some predicate code here*/);
...
}
No. (ok, that might be a bit too short for StackOverflow).
There's no way to determine the "container" that an iterator refers to, because they need not refer to a container at all.
Example:
int foo, bar;
std::copy(&foo, &foo+1, &bar);
In this example, none of the iterators passed to std::copy refer to a "container".
It appears that std::span in C++20 is defined similarly to
template<class T>
class span
{
T* begin;
size_t count;
};
And not
template<class Iter>
class span
{
Iter begin;
Iter end;
};
which is more general (works with std::list, std::map, etc)?
The whole point of std::span<T> is to be a view over contiguous data. pair<T*, size_> (or something like it) is the right way to represent that view. You cannot have a std::span that is a view over a std::list or a std::map, so it doesn't make sense to come up with a way to represent it. The point is to be a common, vocabulary type to just accept contiguous data.
It's also very important span is effectively type-erased. A span<int> could refer into a int[20] or a vector<int> or a int[] that is dynamically allocated somewhere or a llvm::SmallVector<int> or a ... It doesn't matter where it comes from, you just have the one type that is: "view over some contiguous ints".
It is true that pair<Iter, Iter> (or, more generally, pair<Iter, Sentinel>) is a more general representation that would work for more containers. There is such a thing in C++20 as well, it's called std::ranges::subrange<I, S>. But note here that we don't have the type-erasure aspect... a subrange over a map<K, V> will have a different type than a subrange over a different container with the same value_type, like list<pair<K const, V>> or vector<pair<K const, V>> or multimap<K, V>.
According to most C++ references, for instance cplusplus.com, forward iterators are not required to be assignable (I mean, deferenced to an lvalue). However, for several STL algorithms that need to write values, for instance std::fill (also std::generate etc.), the specification uses forward iterator:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
while the equivalent behavior requires lvalue dereference:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val)
{
while (first != last) {
*first = val;
++first;
}
}
So, it is actually using a mutable forward iterator with a single pass.
Now the questions are:
(1) Why not make it clear that the forward iterators used in these cases are mutable?
(2) Update: I found the following question to be stupid: I temporarily forgot that output iterators do not need to support equality comparison. The above question remains, anyway.
Why use forward iterators instead of output iterators for std::fill, std::generate etc. while they do not actually need multiple passes? (std::copy only needs output iterators, for instance. What's the rationale?)
From the signature
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
you cannot infer that ForwardIterator is an iterator described in forward iterator. However, if you read the parameter description, you will find that first and last must be
Forward Iterators to the initial and final positions in a sequence of elements that support being assigned a value of type T.
(emphasis by me). So a forward iterator that fulfills nothing more than what is required of a forward iterator is not a valid argument.
It doesn't seem terribly strange to me, given that the specification for fill is that the (dereferenced) iterator be assignable from T. An output iterator won't suffice because it's not comparable to determine the range end, so a forward_iterator with requirements was chosen.
You'll note that fill_n does use output iterators because no iterator comparison is needed to determine the end of the sequence to fill.
I'm trying to add some debug instrumentation for a vector. My class "has a" vector and offers functions such as:
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
Vector and strings are containers with contiguous memory. When first and last are from a vector (or other container with contiguous memory), I can perform additional sanity checks on the iterators. For example, I can check:
last > first
[first, last) don't overlap with existing elements
count = last - first + 1 is sane
I want to provide a specialization for the additional diagnostics and instrumentation when the container uses contiguous memory, but I don't know what the iterator is called (and have not been able to locate it grepping through sources):
template <typename SequentialIterator>
void assign(SequentialIterator first, SequentialIterator last)
What is the name of the 'SequentialIterator' or 'ContiguousIterator'?
You could use tag dispatching and some standard type traits to choose the appropriate implementation of assign() based on the category of the iterator.
For instance, this basic solution lets you provide two different implementations for random access iterators and non-random access iterators:
#include <type_traits>
#include <iterator>
struct X
{
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
assign_impl(
first, last,
typename std::iterator_traits<InputIterator>::iterator_category()
);
}
template <typename InputIterator>
void assign_impl(InputIterator first, InputIterator last,
std::random_access_iterator_tag)
{
// Implementation for random access iterator...
}
template <typename InputIterator>
void assign_impl(InputIterator first, InputIterator last,
std::input_iterator_tag)
{
// Implementation for non-random access iterator...
}
};
There is no guarantee that the elements of the sequence under a particular iterator are contiguous. There are only guarantees on the operations you can perform with an iterator. There are four main iterator types:
Random Access
Bidirectional
Forward
Input
They can each (except Input) also satisfy the requirements of an Output Iterator, which makes them mutable iterators.
The closest iterator to what you're asking for is a Random Access Iterator. It supports comparison with > and < and allows you to add and subtract iterators from each other. You can even use the array subscript operator with them. They give the illusion that the elements are stored contiguously, but there's no guarantee that they really are.