Keys / Values Functionality to Iterators in C++ - c++

I know this questions has come up in various guises before, but this is slightly different.
I have a class which contains a std::map. Although I wish to use the map for other purposes inside the class, externally I want to expose an iterator adapter to just the values inside the map (ie the second item in the std::pair).
For example in python I might do something like this:
def __iter__(self):
return self._dict.itervalues()
How do I go about doing this in c++, hiding the implementation inside the class?
Thanks,
Dan

Have a look at Boost's transform_iterator which provides exactly this kind of functionality:
template <typename K, typename V>
struct get_value {
const V& operator ()(std::pair<K, V> const& p) { return p.second; }
};
class your_class {
typedef map<int, float> TMap;
TMap mymap;
public:
typedef get_value<TMap::key_type, TMap::data_type> F;
typedef
boost::transform_iterator<F, TMap::iterator>
value_iterator;
value_iterator begin() { return make_transform_iterator(mymap.begin(), F()); }
value_iterator end() { return make_transform_iterator(mymap.end(), F()); }
// TODO Same for const versions.
// Rest of the interface …
};
Now you can iterate over the values, e.g. like this:
your_class c;
// Fill c with some values …
copy(c.begin(), c.end(), ostream_iterator<float>(cout, " "));

Related

Comparer that takes the wanted attribute

In order to use a standard function like std::sort on some standard container Container<T>
struct T{
int x,y;
};
based on the y value, you need to write something like (for example):
std::vector<T> v;
//fill v
std::sort(v.begin(),v.end(),[](const auto& l,const auto& r){
return l.y<r.y;
});
The comparer that was written as lambda function is used too much and re-written again and again during the code for various classes and attributes.
Considering the case where y's type is comparable (either like int or there is an overload for the < operator), is there any way to achieve something like:
std::sort(v.begin(),v.end(),imaginary::less(T::y)); // Imaginary code
Is it possible in C++ to write such a function like less? or anything similar?
I am asking because I remember something like that in some managed language (I am not sure maybe C# or Java). However, I am not sure even about this information if it is true or not.
template<typename T, typename MT>
struct memberwise_less
{
MT T::* const mptr;
auto operator()(const T& left, const T& right) const
{ return (left.*mptr) < (right.*mptr); }
};
template<typename T, typename MT>
memberwise_less<T, MT> member_less(MT T::*mptr)
{
return { mptr };
}
and then you can do
std::sort(v.begin(), v.end(), member_less(&T::y));

Mapping a vector of one type to another using lambda

I have a bit of code that looks like
B Convert(const A& a) {
B b;
// implementation omitted.
return b;
}
vector<B> Convert(const vector<A>& to_convert) {
vector<B> ret;
for (const A& a : to_convert) {
ret.push_back(Convert(a));
}
retun ret;
}
I was trying to rewrite this using lambdas but the code does not look more concise or more clear at all:
vector<B> Convert(const vector<A>& to_convert) {
vector<B> ret;
std::transform(to_convert.begin(),
to_convert.end(),
std::back_inserter(ret),
[](const A& a) -> B { return Convert(a); });
retun ret;
}
What I would really like to do is something like:
vector<B> Convert(const vector<A>& to_convert) {
return map(to_convert, [](const A& a) -> B { return Convert(a); });
}
Where map is a functional style map function that could be implemented as:
template<typename T1, typename T2>
vector<T2> map(const vector<T1>& to_convert,
std::function<T2(const T1&)> converter) {
vector<T2> ret;
std::transform(to_convert.begin(),
to_convert.end(),
std::back_inserter(ret),
converter);
retun ret;
}
Obviously the above is limited because it only works with vector, ideally one would want similar functions for all container types. At the end of the day, the above is still not better than my original code.
Why isn't there something like this (that I could find) in the stl?
You said it yourself, this map is not generic enough. std::transform on the other hand is, at the cost of more verbose interface. Another reason is that map, unlike std::transform forces new allocation, which is not always desirable.
template<class F, class R, class Out>
struct convert_t {
F f;
R r;
// TODO: upgrade std::begin calls with Koenig lookup
template<class D>
operator D()&&{
D d;
std::transform(
std::begin(std::forward<R>(r)),
std::end(std::forward<R>(r)),
std::back_inserter(d),
std::forward<F>(f)
);
return d;
}
template<template<class...>class Z, class Result=Z<
typename std::decay<Out>::type
>>
Result to()&&{return std::move(*this);}
};
template<class F, class R,
class dF=typename std::decay<F>::type,
class dR=typename std::decay<R>::type,
class R_T=decltype(*std::begin(std::declval<dR>())),
class Out=typename std::result_of<dF&(R_T)>::type
>
convert_t<dF,dR,Out>
convert( F&& f, R&& r ) { return {std::forward<F>(f), std::forward<R>(r)}; }
which gives us this:
std::vector<int> vec{1,2,3};
auto r = convert(Convert, vec).to<std::vector>();
for (auto&& x:r)
std::cout << x << '\n';
std::vector<double> r2 = convert(Convert, vec);
for (auto&& x:r)
std::cout << x << '\n';
live example.
This only handles sequence container output, as std::back_inserter would have to be swapped for std::inserter or somesuch for associative containers.
Also, some associative containers (like map) don't like being passed a pair -- they want Key,Value. Expressing that generically is tricky.
The standard library takes care to separate containers, and their traversal. By having std algorithms take containers directly, you'd lose the possibility of using different iterator types for different methods of traversal.
For example, Boost.Iterator uses specifically this clean separation to provide a neat collection of every traversal method you could dream of.
Note also that not all iterators traverse actual containers : std::back_inserter (which you should use instead of ret.begin() if you don't want to fall into unallocated space) actually constructs the container as it goes, and std::ostream_iterator is completely unrelated to any container as it pushes what's assigned to it to a stream.
Of course, nothing stops you from making a thin wrapper for the classic begin/end traversal :
template <
template <class...> class Container,
class Transform,
class ContainerT,
class... ContainerParams
>
auto map(Container<ContainerT, ContainerParams...> const &container, Transform &&transform) {
using DestT = std::result_of_t<Transform(ContainerT const&)>;
Container<DestT, ContainerParams...> res;
using std::begin;
using std::end;
std::transform(
begin(container),
end(container),
std::inserter(res, end(res)),
std::forward<Transform>(transform)
);
return res;
}
(live on Coliru)

Does it make sense to provide only const iterators for a container?

I have a container similar to next one:
class MySpecialContainer
{
std::vector<std::tuple<InternalType, Type1, Type2>> _vec;
};
where Type1 and Type2 are usable outside the container and InternalType is used only inside the container. To iterate through the elements from outside I'm using a member function similar to next one:
void MySpecialContainer::iterate(std::function<void(const Type1&, const Type2&)> fun)
{
for(auto& it : _vec)
{
fun(std::get<1>(it), std::get<2>(it));
}
}
As you can see this approach has several limitations, like not being able to iterate on a subrange or not being able to use non mutating std::algorithms.
Considering MySpecialContainer elements are non mutable from outside from logical considerations does it make sense to provide only const_iterator for it?
If the answer if yes for the first question, is it better to...?
separate _vec into 2 containers, one for InternalType and one for std::pair<Type1, Type2>, keep them synchronized and just return const_iterator for second vector
keep the vector as it is now and make a custom iterator that exposes only const Type1 and const Type2
Just exposing const iterators is fine. There is even precedence in the standard for this, as std::set effectively does this. Technically, iterator and const_iterator can be different types, but you are not allowed to modify the elements through either type of iterator, as that could break the invariants for set.
One option is to expose iterators that give access to certain fields only of your elements, e.g.:
#include <vector>
#include <tuple>
#include <boost/iterator/transform_iterator.hpp>
struct Type1 {};
struct Type2 {};
struct InternalType {};
class MySpecialContainer
{
typedef std::vector<std::tuple<InternalType, Type1, Type2>> Vec;
Vec _vec;
struct Extractor
{
std::tuple<Type1&, Type2&> operator()(Vec::value_type& t) const {
return std::tie(std::get<1>(t), std::get<2>(t));
}
std::tuple<Type1 const&, Type2 const&> operator()(Vec::value_type const& t) const {
return std::tie(std::get<1>(t), std::get<2>(t));
}
};
public:
typedef boost::transform_iterator<Extractor, Vec::iterator> iterator;
typedef boost::transform_iterator<Extractor, Vec::const_iterator> const_iterator;
iterator begin() { return iterator{_vec.begin()}; }
iterator end() { return iterator{_vec.end()}; }
const_iterator begin() const { return const_iterator{_vec.begin()}; }
const_iterator end() const { return const_iterator{_vec.end()}; }
};
int main() {
MySpecialContainer c;
for(auto x : c) {
}
}
Note that through non-const iterators you can still update the exposed values because the trasform iterator returns a tuple of references.

Const converting std containers

Consider that I have a std::vector.
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
I now want to pass the vector somewhere and disallow modifying the contents of the objects its contains while still allowing to modify the container when able:
// Acceptable use:
void call_something() {
std::vector<int> blah;
blah.push_back(1);
blah.push_back(2);
// Currently, compiler error because of mismatching types
something(blah);
}
void something(std::vector<const int>& blah)
{
// Auto translates to 'const int'
for ( auto& i : blah ) {
// User cannot modify i.
std::cout << i << std::endl;
}
blah.push_back(blah.size()); // This should be acceptable
blah.emplace_back(); // This should be acceptable
return;
}
// Unacceptable use:
void something_else(const std::vector<int>& blah)
{
// Because of const vector, auto translates to 'const int'
for ( auto& i : blah ) {
std::cout << i std::endl;
}
blah.push_back(blah.size()); // This will present an unacceptable compiler error.
blah.emplace_back(); // This will present an unacceptable compiler error.
return;
}
Is there an easy way to do this?
To enable the operations you wish to allow while preventing the others, you need to take a fine-grained approach to your function's interface. For example, if your calling code were to pass const iterators (begin and end) as well as a back inserter (or custom back emplacer functor), then exactly the subset of operations you showed would be possible.
template <class Iter, class F>
void something(Iter begin, Iter end, F&& append)
{
using value_type = typename std::iterator_traits<Iter>::value_type;
std::copy(begin, end, std::ostream_iterator<value_type>(std::cout, "\n"));
append(std::distance(begin, end));
append();
return;
}
That said I don't find your examples particularly compelling. Do you have a real scenario in which you must maintain mutable elements, pass a mutable container to a function, yet treat the passed elements as immutable?
There is no easy way to do this. One way would be to wrap a vector in a type that exposes only the functionality that you want to allow. For instance
template<typename T, typename A = std::allocator<T>>
struct vector_wrap
{
using iterator = typename std::vector<T, A>::const_iterator;
using const_iterator = typename std::vector<T, A>::const_iterator;
using size_type = typename std::vector<T, A>::size_type;
vector_wrap(std::vector<T, A>& vec)
: vec_(&vec)
{}
void push_back(T const& value) { vec_->push_back(value); }
void push_back(T&& value) { vec_->push_back(std::move(value)); }
size_type size() { return vec_->size(); }
iterator begin() const { return vec_->cbegin(); }
iterator end() const { return vec_->cend(); }
private:
std::vector<T, A> *vec_;
};
Since the above implementation only stores a pointer to the vector it wraps, you'll have to ensure that the lifetime of the vector is longer than that of vector_wrap.
You'll have to modify something and something_else so that they take a vector_wrap<int> as argument. Since vector_wrap::begin and vector_wrap::end return const_iterators, you'll not be allowed to modify existing elements within the for statement.
Live demo

Possible: Set Operations on Disparate Maps with Same Key Type?

Let's say I have two maps:
typedef int Id;
std::map<Id, std::string> idToStringMap;
std::map<Id, double> idToDoubleMap;
And let's say I would like to do a set operation on the keys of the two maps. Is there an easier way to do this than to create a custom "inserter" iterator? such that I could do something like:
std::set<Id> resultSet;
set_difference( idToStringMap.begin(), idToStringMap.end(),
idToDoubleMap.begin(), idToDoubleMap.end(), resultSet.begin() );
My experimentation results imply that it will be necessary to create a custom inserter and perhaps a custom key comparer to do this, but I want for some insight/shortcut before doing so.
I don't think this is possible using just the stl without a custom iterator. You should create a generic select_1st_iterator. This would wrap any iterator to a pair and return the itr->first when dereferenced.
Note: some extensions to the stl have a select1st functor that takes a pair and returns the first element. But I have not seen an iterator version.
If you are planning to write an iterator I would suggest that you use the boost iterator library. The most likely candidate for the select_1st_iterator is the transfor_iterator
Assuming select_1st_iterator is a function that creates the real select_1st_iterator_t type, it could look like:
NOTE: your code will crash if you don't use an insert_iterator for resultSet
template<class T>
select_1st_iterator_t<T> select_1st_iterator<T>(itr)
{
return select_1st_iterator_t<T>(itr);
}
std::set<Id> resultSet;
set_difference(
select_1st_iterator(idToStringMap.begin()),
select_1st_iterator(idToStringMap.end()),
select_1st_iterator(idToDoubleMap.begin()),
select_1st_iterator(idToDoubleMap.end()),
std::inserter(resultSet, resultSet.begin()) );
My solution using iain's advice:
template <typename T>
class Select1st
: public std::unary_function<T&,typename T::first_type>
{
public:
int operator() (T & value) const
{
return value.first;
}
};
template <typename T>
class KeyGrabItorAdapter
: public boost::transform_iterator< Select1st<typename T::value_type>,
typename T::iterator >
{
public:
KeyGrabItorAdapter( typename T::iterator itor )
: boost::transform_iterator<Select1st<typename T::value_type>,
typename T::iterator>
( itor, Select1st<typename T::value_type>() )
{
}
};
having the preceeding allows the following:
typedef std::map<int, int> IntToIntMap;
IntToIntMap intToIntMapA;
IntToIntMap intToIntMapB;
typedef std::map<int, double> IntToDoubleMap;
IntToDoubleMap intToDoubleMapA;
IntToDoubleMap intToDoubleMapB;
KeyGrabItorAdapter<IntToIntMap> grabFirstABegin( intToIntMapA.begin() ) ;
KeyGrabItorAdapter<IntToIntMap> grabFirstAEnd( intToIntMapA.end() ) ;
KeyGrabItorAdapter<IntToDoubleMap> grabFirstBBegin( intToDoubleMapB.begin() ) ;
KeyGrabItorAdapter<IntToDoubleMap> grabFirstBEnd( intToDoubleMapB.end() ) ;
std::set<int> intResultSet;
set_difference( grabFirstABegin, grabFirstAEnd,
grabFirstBBegin, grabFirstBEnd,
inserter( intResultSet, intResultSet.begin()),
intToIntMapA.key_comp() );