3

Say I have

class Value;
class Key;

class MyClass {
  private:
  std::map<Key,Value> my_map;
  ....
}

Inside of MyClass methods I have a very convenient way to iterate through values of my_map by saying

 for( auto& value: my_map | boost::adaptors::map_values) {
    ...
 }

However I would like to have a method of MyClass that would essentially output my_map | boost::adaptors::map_values and allow convenient value iteration outside of MyClass methods. How do I declare such a method? Would I need to implement some sort of pseudo container and corresponding iterator or there is a shortcut?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
LRaiz
  • 667
  • 7
  • 18

2 Answers2

1

Basically, you have two good choices. You can provide users with a nicely decoupled interface by using boost's any_range, or else if you need the best performance, you can give clients direct access to the adapted range using c++11 decltypes. In the first case, clients won't have to change if you change implementation, but this comes at a cost of extra redirection.

using any_range:

#include <boost/range.hpp> 
#include <map> 
#include <boost/range/adaptor/map.hpp> 
#include <boost/range/any_range.hpp>

class Foo 
{ 
    std::map<int, int> my_map;
    boost::any_range<int, boost::forward_traversal_tag, int&, std::ptrdiff_t> Values()
    { return my_map | boost::adaptors::map_values; }
}; 

giving direct access (awkward syntax here is because you require VS2013, which doesn't support member variables in unevaluated context at class scope):

#include <boost/range.hpp> 
#include <map> 
#include <boost/range/adaptor/map.hpp> 

class Foo 
{ 
    std::map<int, int> my_map;
    auto Values() -> decltype(my_map | boost::adaptors::map_values)
    { return my_map | boost::adaptors::map_values; }
}; 
rmcclellan
  • 445
  • 5
  • 9
  • Unfortunately this won't compile in VS2013 – LRaiz Feb 10 '14 at 14:49
  • 1
    Sorry, I didn't realize you were using VS2013. In general, if you want answers to work in VS2013, you should mention that in your question as Microsoft still doesn't support the C++11 standard. My code would have worked on any C++11 compiler, but I just edited it to work in visual studio as well. – rmcclellan Feb 10 '14 at 16:01
  • @rmcclelan Thanks for your help. I still can't make it to work. I placed your typedef as well as Values() method inside of class declaration right after declaration of my_map and it does not compile in VS2013. – LRaiz Feb 10 '14 at 20:03
  • LRaiz, could you provide an error message? Thanks. – rmcclellan Feb 10 '14 at 21:07
  • First here is my code (soory still can't master markdown) #include #include #include class Foo { std::map my_map; typedef boost::iterator_range values_range; values_range Values() { return values_range(my_map | boost::adaptors::map_values); } }; – LRaiz Feb 11 '14 at 01:35
  • First couple of errors Error 1 error C2678: binary '|' : no operator found which takes a left-hand operand of type 'unknown' (or there is no acceptable conversion) Error 2 error C2228: left of '.begin' must have class/struct/union – LRaiz Feb 11 '14 at 01:39
  • okay, edited again with a re-write that should be okay in VS2013. – rmcclellan Feb 11 '14 at 16:36
  • Super. Thanks for finding a workaround for VS2013 C++11 deficiencies. – LRaiz Feb 11 '14 at 20:25
  • @rmcclelan - VS2013 gives me headaches. 'ugly' options compiles and is usable in range for statement. However it does not allow Values().size() forcing workarounds. – LRaiz Feb 12 '14 at 16:05
  • boost::any_range Values() { return my_map | boost::adaptors::map_values; } leads to compiler error Error 1 error C2039: 'distance_to' : is not a member of 'boost::range_detail::any_forward_iterator_interface' – LRaiz Feb 12 '14 at 16:13
  • @rmcclelan provided a solution that works well but now I am having troubles trying to extend it. What would be a correct syntax to get range iterators for const values as opposed to mutable ones? Especially which solution would work with the latest VS2013 which still does not like boost::anyRange? – LRaiz Feb 05 '15 at 17:21
1

It isn't strictly necessary to use a pseudo container or an iterator adapter, such as boost::iterator_range. Although using a proper adapter such as iterator_range would theoretically be more correct and versatile, and will not violate the principle of least knowledge as applied to objects, you may want to avoid it because of an additional indirection, or maybe because your iterator is generally only a single-pass range and not a forward range.

So if you wish to use the adapted range directly you can simply use decltype to deduce the iterator type already returned by the adaptor:

#include <iostream>
#include <map>

#include <boost/range/adaptor/map.hpp>

class A {
  std::map<int,int> my_map = { {0, 1}, {2, 3} };
public:
  decltype(my_map | boost::adaptors::map_values)
  values() { return my_map | boost::adaptors::map_values; }
};

int main() {
    for (const auto& v : A().values())
        std::cout << "v = " << v << std::endl;
    return 0;
}
/* Output:
v = 1
v = 3
*/

And if you want the exposed values to be a const member function it's slightly more intricate:

class A { 
...
  decltype(const_cast<const std::map<int,int>&>(my_map) | boost::adaptors::map_values)
  values() const { return my_map | boost::adaptors::map_values; }
}
mockinterface
  • 14,452
  • 5
  • 28
  • 49
  • Unfortunately the code below doesn't compile with VS2013. #include #include class Bar { std::map _myMap; decltype(_myMap | boost::adaptors::map_values) values() { return _myMap | boost::adaptors::map_values; } } – LRaiz Feb 10 '14 at 14:29
  • Error 1 error C2678: binary '|' : no operator found which takes a left-hand operand of type 'unknown' (or there is no acceptable conversion) Error 2 error C2440: 'return' : cannot convert from 'boost::range_detail::select_second_mutable_range,std::allocator>>>' to 'int' – LRaiz Feb 10 '14 at 14:32
  • @LRaiz 1. Of course this code is valid C++11, it compiles and works, [see it live on coliru](http://coliru.stacked-crooked.com/a/eed284f289b110ab), 2. the tags you have specified for the question did not include VS, so I don't see why you have unaccepted it. It may be best to investigate the limitations of visual-studio in a separate question. (And you need the Nov CTP release to get the best subset of C++11 feature for VS.) – mockinterface Feb 10 '14 at 20:33