How to declare boost range adaptor (e.g. map_values) - c++

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?

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; }
};

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; }
}

Related

How to filter out elements of certain data types in a vector of std::variant?

I have a std::vector of std::variant elements with type int or std::set<int>. I want to loop over this vector and insert an extra item if the iterated element is of type std::set<int>. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
#include <variant>
#include <set>
#include <vector>
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for (int i = 0; i < var_vec.size(); ++i)
{
// if the element var_vec[i] is of type std::set<int>
if (var_vec[i].index() == 1) var_vec[i].insert(888); // !ERROR! How to achieve this?
}
return 0;
}
Error message:
error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'
There's nothing wrong with calling index() at runtime, this happens all the time. The real problem is:
var_vec[i].insert(888);
var_vec[i] is a std::variant. It does not have a method called insert().
std::get<1>(var_vec[i]).insert(888);
This gives you the variant's set, which will happily allow you to insert() something.
One problem with this overall approach is that if you, for some reason, want to modify your variant, and that std::set is no longer its 2nd alternative, for some reason, all of the above logic will break again.
You should consider using std::holds_alternative, instead of index(), and using a type with std::get instead of an index, which will automatically adapt to this kind of a change.
I want to loop over this vector and insert an extra item if the iterated element is of type std::set. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
You don't need to check the type at runtime, you could just use std::visit() and check if the variant's type is std::set<int> at compile-time:
// Others headers go here...
#include <type_traits>
// ...
for (int i = 0; i < var_vec.size(); ++i)
// if the element var_vec[i] is of type std::set<int>
std::visit([](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::set<int>>)
arg.insert(888);
}, var_vec[i]);
The filtering can be done via ranges::views::filter, and we can use std::visit to make up a predicate.
To do so we need to feed std::visit with a predicate on the types. A predicate on types can be made up as a set of overlaoded functions that each takes a type and all return a bool; you can create it via boost::hana::overload.
However, to filter the variants we need to feed std::visit only with its first argument, and to leave its second parameter free; one way to do it is to wrap std::visit in a nested lambda, like this:
auto visit_with = [](auto const& fs){
return [&fs](auto const& xs) {
return std::visit(fs, xs);
};
};
Another way is to use BOOST_HOF_LIFT and boost::hana::curry the result to make it partially applicabale, which is all done in one line:
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
Here's the complete code:
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <set>
#include <variant>
#include <vector>
using namespace boost::hana;
using namespace ranges::views;
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
// predicate
auto constexpr true_for_sets = overload(
[](std::set<int> const&){ return true; },
[](auto const&){ return false; });
// function object wrapping std::visit
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
// filter
auto var_vec_out = var_vec | filter(visit_with(true_for_sets));
}
And this is all about the filtering you mention in the title. However, in the body of the question, you're not actually doing a filtering, but something else, as you are modifying the set as you traverse it.
As was already pointed out you're calling insert on an std::variant and not the std::set.
One approach I'd use is using std::get_if like so:
#include <variant>
#include <set>
#include <vector>
int main( ) {
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for ( auto& v : var_vec ) {
// If the variant does not hold a set, get_if returns a nullptr.
if ( auto set{ std::get_if<std::set<int>>( &v ) } ) {
set->insert( 888 );
}
}
// Or you could use holds_alternative with get, but this isn't as clean.
for ( auto& v : var_vec ) {
if ( std::holds_alternative<std::set<int>>( v ) ) {
auto set{ std::get<std::set<int>>( v ) };
set.insert( 999 );
}
}
}
This allows you to both test for the type and use the contained alternative.

How to most concisely initialize std::map using an initializer list that includes a constructor call for the mapped_type

The vector constructor calls in the statement below construct vectors with 50 elements of identical values in each vector as values in the sviMap. The statement repeats vector<int> three times. Is there a more concise way to achieve this overall initialization of the sviMap where vector<int> appears only once in the statement?
map<string, vector<int>> sviMap { {"Leo", vector<int>(50, 101)} , {"Brad", vector<int>(50, 201)} };
Strictly speaking, no. You need to have template specialization and two instances of the vector.
But if you need to have "this text once", this workaround could work for you:
using vi = vector<int>;
map<string, vi> sviMap{ {"Leo", vi(50, 101)} , {"Brad", vi(50, 201)} };
Formally, this is not "the statement", but two statements, but the vector appears only once. If your goal is to have one point for changes, this could work.
I wanted to correct my mistake, so here's another solution.
And this one will result in vectors of the correct length.
I just used inheritance to create a vector type with a constructor that has the desired behavior, so you can then use initializer lists without extra specifics
#include <cassert>
#include <map>
#include <vector>
#include <string>
template<typename type_t>
class my_vector :
public std::vector<type_t>
{
public:
my_vector() = default;
my_vector(const size_t size, const type_t value) :
std::vector<type_t>(size, value)
{
}
};
int main()
{
std::map<std::string, my_vector<int>> sviMap{ { "Leo", {50, 101}}, { "Brad", {50, 201}} };
auto vec = sviMap["Leo"];
auto value = vec[23];
}
You can do it like this, just use nested initializers
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> sviMap
{
{"Leo", {50, 101}},
{"Brad", {50, 201}}
};
auto vec = sviMap["Leo"];
}

Can I use lambda as a hashing function in boost::multi_index hash-like interface?

Is it possible to use lambda for hashing in hashed_<non>_unique interface for boost::multi_index?
See this example: https://godbolt.org/z/1voof3
I also saw this: How to use lambda function as hash function in unordered_map? where the answer says:
You need to pass lambda object to unordered_map constructor since lambda types are not default constructible.
and I'm not sure is it even possible to do for the given example on godbolt.
Starting with C++20, yes, you can: https://godbolt.org/z/fTbzPP (note f is declared as auto const hash_f, without a &).
As for #sehe's claim that multi_index_containers can't be passed instances of hash objects (or other intervening function objects) at construction time, the claim is incorrect: they can, although the interface is somewhat complicated:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>
struct non_default_ctble_hash
{
non_default_ctble_hash(std::size_t n):n{n}{}
template<typename T>
std::size_t operator()(const T& x){return std::hash<T>{}(x)*n;}
std::size_t n;
};
using namespace boost::multi_index;
using container=multi_index_container<
int,
indexed_by<
hashed_unique<identity<int>,non_default_ctble_hash>
>
>;
int main()
{
container::ctor_args_list cal{
{0,identity<int>{},non_default_ctble_hash{666},std::equal_to<int>{}}
};
container c(cal);
}
I don't think you can. With a standard container you would have had to supply the actual instance to the constructor. However, MultiIndex doesn't afford that:
docs
As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided. Upon construction, max_load_factor() is 1.0.
Loophole?
You can perhaps get away with a locally defined class:
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
Which does work: c++20 required
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
#include <string>
using namespace boost::multi_index;
using LegsType = int;
struct Animal {
std::string name;
LegsType legs;
};
// tags
struct animal_legs {};
int main() {
// using lambda doesn't work for hashing
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
animals.insert({ "cat", 4 });
auto const& legs_index = animals.get<animal_legs>();
int num_of_legs = 4;
std::cout << "Number of animals that have " << num_of_legs
<< " legs is: " << legs_index.count(num_of_legs) << '\n';
}
Prints
Number of animals that have 4 legs is: 1

Iterator for C++11 map values (simple and transparent)

I am looking for a simple way to create an iterator for the values of a map in C++11.
This method should be simple and transparent: simple in that it should be easy to implement, and transparent in that the client should not know the values come from a map, not a set.
This question has been asked several times before. Many of these questions predate C++11 and use boost, which I do not want to use. Some are not simple, John Ahlgren's solution here, http://john-ahlgren.blogspot.com/2013/10/how-to-iterate-over-values-of-stdmap.html , for example requires a page of code to write a custom iterator.
The others are not transparent, i.e., clearly one can write:
map<string,foo> mymap;
for (auto it=mymap.begin();it!=mymap.end();++it){
Foo val= it->second;
...
}
However, I do not want to do this because I do not want the client to have to know of the data representation.
The problem comes up as follows.
I have a bunch of objects uniquely indexed with a long "key". Sometimes I want to manipulate sets of these objects. Other times I want to retrieve an object given its key.
I cannot use the "set" class directly for several reasons, chief among which is that it does not store mutable instances, and these instances must be mutable (except, obviously, for the key).
So, I have decided to store all my objects in a giant global hashtable:
map<long,Foo> all_the_objects;
I then do not work with set<Foo> at all. Instead I work with set<long> and use an adaptor to simulate a set of Foo, i.e.,
class SetOfFoo{
private: set<long> theKeys;
public:
void insert(const & Foo);
size_t size() return theKeys.size();
bool is_member(const & Foo)
{return theKeys.find(Foo.key)
!= theKeys.end;}
Foo & insert(const & Foo val){
long key=val.key;
all_the_objects[key]=val;
return all_the_objects[key];
}
...::iterator begin() {???}
}
In other words, the client of the SetOfFoo class does not know or need to know that SetOfFoo is implemented as as set of keys.
I also cannot just make a Vector myself in the adaptor class, because one cannot store references in C++ collections.
Is it really impossible to make a simple, transparent way to iterate over map<> values? I find it hard to believe, as this is a very common need and is trivial to do in every language I have seen that has hashtables. I just don't understand how this can be hard.
it's pretty trivial.
Here's an extremely simplistic version that minimally solves the problem for a map of ints to strings. You can either rewrite for the types you want or templatise it as you wish.
#include <map>
#include <iostream>
#include <iterator>
#include <string>
#include <algorithm>
struct map_value_iterator : public std::map<int, std::string>::const_iterator
{
map_value_iterator(std::map<int, std::string>::const_iterator src)
: std::map<int, std::string>::const_iterator(std::move(src))
{
}
// override the indirection operator
const std::string& operator*() const {
return std::map<int, std::string>::const_iterator::operator*().second;
}
};
using namespace std;
int main()
{
map<int, string> myMap { {1, "Hello" }, { 2, "World" } };
copy(map_value_iterator(begin(myMap)), map_value_iterator(end(myMap)), ostream_iterator<string>(cout , " "));
cout << endl;
return 0;
}
Program output:
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
Executing the program....
$demo
Hello World
You can do something like the following (C++98):
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include "util/pair_iterator.hpp"
template<class T> inline T const& constify(T& t) { return t; }
int main()
{
using namespace std;
using namespace util;
map<int, string> m;
m[0] = "alice";
m[1] = "bob";
m[2] = "carol";
m[3] = "dave";
m[4] = "eve";
copy(
over_second(m.begin())
, over_second(m.end())
, ostream_iterator<string>(cout, "\n")
);
copy(
over_first(m.begin())
, over_first(m.end())
, ostream_iterator<int>(cout, "\n")
);
// const iterators check
copy(
over_second(constify(m).begin())
, over_second(constify(m).end())
, ostream_iterator<string>(cout, "\n")
);
copy(
over_first(constify(m).begin())
, over_first(constify(m).end())
, ostream_iterator<int>(cout, "\n")
);
}
Here is an implementation:
// util/pair_iterator.hpp
#include <iterator>
#include "boost/iterator/transform_iterator.hpp"
#include "boost/type_traits/remove_reference.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/mpl/if.hpp"
namespace util {
namespace aux {
template<class T> struct dereference_type
: boost::remove_reference<typename std::iterator_traits<T>::reference>
{
};
template<class PairT>
struct first_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::first_type const
, typename PairT::first_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.first; }
};
template<class PairT>
struct second_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::second_type const
, typename PairT::second_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.second; }
};
} // namespace aux {
template<class IteratorT>
inline
boost::transform_iterator<aux::first_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_first(IteratorT const& i)
{
typedef aux::first_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}
template<class IteratorT>
inline
boost::transform_iterator<aux::second_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_second(IteratorT const& i)
{
typedef aux::second_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}
} // namespace util

Is it possible to use boost::filter_iterator for output?

I am using std::transform with an std::back_inserter to append elements to an std::deque. Now the transformation may fail and will return a invalid object (say an uninitialized boost::optional or a null pointer) in some cases. I would like to filter out the invalid objects from getting appended.
I thought about using boost::filter_iterator, but not sure how to present the end() parameter of the filtered range.
The documentation of boost::filter_iterator suggests that output filtering is possible. Should I just specialize operator == for std::back_insert_iterator in this case to always return false?
In addition to this, if I want to append values of initialized boost::optional or pointers, can I chain boost::filter_iterator and boost::indirect_iterator?
I am trying to avoid rolling out my own transform_valid function that takes an optional extractor function.
Is it even possible to use filter_iterator as an output iterator?
I suggest using boost range (algorithms & adaptors) for ease of use, you'd write:
boost::copy(
data | transformed(makeT) | filtered(validate) /* | indirected */,
std::back_inserter(queue));
Here is a complete working example of that:
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/optional.hpp>
#include <vector>
#include <deque>
typedef boost::optional<int> T;
typedef std::deque<T> Q;
static T makeT(int i)
{
if (i%2) return T();
else return i;
}
static bool validate(const T& optional)
{
return (bool) optional; // select the optional that had a value set
}
int main()
{
static const int data[] = { 1,2,3,4,5,6,7,8,9 };
Q q;
using boost::adaptors::filtered;
using boost::adaptors::transformed;
// note how Boost Range elegantly supports an int[] as an input range
boost::copy(data | transformed(makeT) | filtered(validate), std::back_inserter(q));
// demo output: 2, 4, 6, 8 printed
for (Q::const_iterator it=q.begin(); it!=q.end(); ++it)
{
std::cout << (*it? "set" : "unset") << "\t" << it->get_value_or(0) << std::endl;
}
return 0;
}
Update
With a little help from this answer: Use boost::optional together with boost::adaptors::indirected
I now include an elegant demonstration of using the indirected range adaptor as well for immediate output of the queue (dereferencing the optionals):
Note that for (smart) pointer types there would obviously be no need to provide the pointee<> specialisation. I reckon this is by design: optional<> is not, and does not model, a pointer
#include <boost/range.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/optional.hpp>
namespace boost {
template<typename P> struct pointee<optional<P> > {
typedef typename optional<P>::value_type type;
};
}
typedef boost::optional<int> T;
static T makeT(int i) { return i%2? T() : i; }
static bool validate(const T& optional) { return (bool) optional; }
int main() {
using namespace boost::adaptors;
static int data[] = { 1,2,3,4,5,6,7,8,9 };
boost::copy(data | transformed(makeT)
| filtered(validate)
| indirected,
std::ostream_iterator<int>(std::cout, ", "));
}