Does Fusion have a tail function? - c++

I need a tail-like funciton that can be used like this:
boost::fusion::vector<char, int, float> a('a', 12, 5.5f);
boost::fusion::vector<int, float> b(12, 5.5f);
boost::fusion::copy( Tail(a), b );

In the documentation for Boost Fusion, there's a section under Algorithms called Transformation. The Functions listed here notably include one called pop_front. This seems to do exactly what we want:
Returns a new sequence, with the first element of the original removed.
...
Example
assert(pop_front(make_vector(1,2,3)) == make_vector(2,3));
For your example:
boost::fusion::vector<char, int, float> a('a', 12, 5.5f);
boost::fusion::vector<int, float> b(12, 5.5f);
boost::fusion::copy( boost::fusion::pop_front(a), b );
The name pop_front is a little strange, considering that it doesn't actually modify the input sequence, but returns a modified result. However, pop_front comes from the C++ standard library, where it is used for removing the first element of a collection such as with std::list::pop_front. Boost Fusion chose this name to be "more consistent" with the standard library.

Related

Providing an allocator for Boost's `cpp_dec_float_100`

I have a dataset stored in .root file format (from the CERN ROOT framework) as type cpp_dec_float_100 (from the boost::multiprecision library). This data is read into an std::vector<cpp_dec_float_100>. By default, cpp_dec_float_100 is unallocated. If I were to try to read this data into a vector as-is, an std::bad_alloc is thrown. So, I've taken the advice of the Boost docs and provided a generic allocator, which seems to solve the issue (and appears to cut the size the resulting vector in half).
Ultimately, I want to pass this vector as an argument to a function that I've written, which performs binary search on a vector to find the element of that vector closest to a given value:
#include <boost/multiprecision/cpp_dec_float.hpp>
using Mult_t = boost::multiprecision::cpp_dec_float<100, int, allocator<boost::multiprecision::number<boost::multiprecision::cpp_dec_float<100>>>>;
std::vector<Mult_t>::iterator search(std::vector<Mult_t> &vec, Mult_t value){
auto it = lower_bound(vec.begin(), vec.end(), value);
if(it != vec.begin()){
if(abs(value - *(it - 1)) < abs(value - *it)){
--it;
}
}
return it;
}
I'm using the "alias" Mult_t as the alternative is a bit of a mouthful.
So, given the vector vec and the value val, this finds the element in vec nearest to val.
If I use the cpp_dec_float_100 type as-is (i.e. Mult_t = boost::multiprecision::cpp_dec_float_100), this works great. However, when I attempt to provide an allocator, I'm given the error:
In module 'std' imported from input_line_1:1:
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../include/c++/4.8.5/bits/stl_algobase.h:965:18: error: invalid operands to binary expression ('boost::multiprecision::backends::cpp_dec_float<100, int,
std::allocator<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100,
int, void>, boost::multiprecision::expression_template_option::et_on> > >' and 'const
boost::multiprecision::backends::cpp_dec_float<100, int,
std::allocator<boost::multiprecision::number<boost::multiprecision::backends::cpp_dec_float<100,
int, void>, boost::multiprecision::expression_template_option::et_on> > >')
if (*__middle < __val)
I don't quite understand what's going on here (I doubt seriously it has anything to do with the allocator), and the error message isn't terribly insightful.
Your problem has nothing to do with allocator, just because cpp_dec_float<...> has no operator<(), only number<cpp_dec_float<...>> supports.
You should redefine your Mult_t as:
using namespace boost::multiprecision;
using Mult_t = number<
cpp_dec_float<100, int, std::allocator<number<cpp_dec_float<100>>>>>;

Is there any way to create an STL map with a composite key and a composite value?

I am trying to create a map, something like: [{11,"Jitendra", 15.5}, {12, "Pranay", 15.5}], where the data between first curly brackets becomes the key and between the second curly brackets becomes values. Whatever technique I am trying it results in error.
Please let me know how I can declare an STL map, insert and manipulate values?
Compound variables are usable as keys for a map as long as they are comparable with operator< (see std::less). The simplest case is using a std::pair:
std::map<std::pair<int, std::string>, int> mymap; // key: int, string; value: int
You can extend the concept with an arbitrary number of key elements by using std::tuple as introduced in C++11.
Both pairs and tuples bring their own comparator overloads (see e.g. for tuple here), so no further work is needed on your side if your tuple elements are comparable by-themselves and are happy with an ordering that gives precedence on the first, then the second, and so on, element of the pair/tuple. Use std::make_pair() or std::make_tuple() to conveniently create them. However, it also works like this (C++17):
std::map<std::tuple<int, int>, std::tuple<int, int>> foo;
// insert
foo[{1,2}] = {3,4};
// access
auto [v1, v2] = foo[{1,2}];
std::cout << v1 << ", " << v2 << std::endl;
To be most flexible, you can extend it even further by using a struct and providing a comparator for the map to order its elements. You can also do this if your compiler lives behind the moon and does not support C++11.

understanding how zip in range-v3 works

I am trying to understand how ranges::views::zip works in range-v3. I understand that it is a range that allows to iterate on several ranges in one single loop by creating a tuple of the elements in different ranges.
std::vector<int> v1 = {0, 1, 2};
std::vector<char> v2 = {'a', 'b', 'c'};
auto zip = ranges::views::zip(v1,v2);
// zip(v1,v2) = [(0,a), (1,b), (2,c)]
ranges::actions::sort(zip);
std::sort(std::begin(zip), std::end(zip));
The sort using ranges::actions works fine but std::sort doesnt compile and gives the following error
/usr/include/c++/9.3.0/bits/stl_algobase.h:151: error: no matching function for call to ‘swap(concepts::return_t<ranges::common_pair<int&, double&>, void>, concepts::return_t<ranges::common_pair<int&, double&>, void>)’
151 | swap(*__a, *__b);
| ~~~~^~~~~~~~~~~~
Why is this happening?
I have also tried to remove elements in both containers at the same time. ranges::actions::unique doesn't compile with the following error:
/home/jjcasmar/projects/cpfsofaplugin/src/CPFSofaPlugin/minimalExample.cpp:27: error: no match for call to ‘(const ranges::actions::action_closure<ranges::actions::unique_fn>) (ranges::zip_view<ranges::ref_view<std::vector<int, std::allocator<int> > >, ranges::ref_view<std::vector<double, std::allocator<double> > > >&)’
27 | ranges::actions::unique(v1Andv2);
| ^
but auto lastIt = std::unique(std::begin(v1Andv2), std::end(v1Andv2)) compiles find, although I dont know how to get the internal iterators of the zip to be able to erase past the end elements.
I dont really understand how this works under the hood and why in some cases std algorithms work fine but in some cases it doesn't. Can someone give some explanation about this?
Look at the types:
auto zip = ranges::views::zip(v1, v2);
// ranges::zip_view<
// ranges::ref_view<std::vector<int>>
// ranges::ref_view<std::vector<char>>
// >
auto begin = std::begin(zip);
// ranges::basic_iterator<
// ranges::iter_zip_with_view<
// ranges::detail::indirect_zip_fn_,
// ranges::ref_view<std::vector<int>>,
// ranges::ref_view<std::vector<char>>
// >::cursor<false>
// >
using traits = std::iterator_traits<decltype(begin)>;
static_assert(std::is_same_v<traits::value_type, std::pair<int, char>>);
static_assert(std::is_same_v<traits::reference, ranges::common_pair<int&, char&>>);
The value_type type is a std::pair of values. The reference type is a ranges::common_pair of references.
std::sort uses std::iter_swap which is specified in terms of dereferencing the iterators and calling std::swap. So std::sort will try to swap two ranges::common_pair of references. On the other hand, ranges::actions::sort uses ranges::iter_swap which is customized to handle pairs and tuples of references.
Pairs and tuples of references are/were second class citizens in the standard library.
ranges::actions::unique requires an erasable range which evidently this does not satisfy.
Added
The documentation for range-v3 is sparse. To find information such as the above, there is of course looking at the source for range-v3, quick experimentation on godbolt.org (range-v3 is an available library), and "standard" C++ tricks to find the type of a variable (e.g., calling a function template which is declared but not defined, with the variable's type as the template argument, and seeing which instantiation is called).
To comment more on unique, ranges::action::unique does not return an iterator. It erases the non-unique elements and returns a range (see the source). In part of the compiler error which was omitted, the error references the fact that the range is not erasable (buried in a huge error).
ranges::unique returns an iterator and can be called without error. This is a basic_iterator<...>. One option is to use ranges::distance to find the distance from the begin zip iterator and use this to get the underlying iterator:
auto zip_uniq_iter = ranges::unique(zip);
auto first_uniq_iter = std::next(v1.begin(), ranges::distance(ranges::begin(zip), zip_uniq_iter));
You cannot use std::sort on views.
But you can transform your view to a vector and then it works:
https://godbolt.org/z/_FvCdD
I can recommend the following sites for more information on ranges:
https://www.walletfox.com/course/quickref_range_v3.php
https://mariusbancila.ro/blog/2019/01/20/cpp-code-samples-before-and-after-ranges/

How to create an STL output iterator for a Eigen matrix?

Say you have an Eigen::Matrix<float, Eigen::Dynamic, 3> m;. Now you have data outside Eigen and want to move it into your matrix using an STL algorithm (e.g. std::transform). The documentation gives examples on how to use m.rowwise() in a range-based for loop. However, passing m.rowwise() as the output iterator of std::transform does not work. It complains that VectorwiseOp does not have an operator++. A range-based for loop would call the begin() method of the range expression anyhow, but the VectorwiseOp does not have a begin() method.
Say I have a std::vector<std::tuple<float, float, float>> i;. How would I transform that into the matrix? Of course one can use a plain loop over an index variable here, but that becomes difficult when using more complex input data structures. Then, one can use std::for_each and maintain the output index externally, but that seems clumsy. I'm looking for something like
std::transform(
i.cbegin(),
i.cend(),
/* TODO: something like m.rowwise() */,
[](const std::tuple<float, float, float> &e) -> /* TODO */ {
/* don't care here */
});
Eigen::VectorwiseOp<ET, D> gains an appropriate begin and end in Eigen 3.4, you were looking at the source for 3.3.
If you upgrade to 3.4, it's simply
std::transform(
i.cbegin(),
i.cend(),
m.rowwise().begin(),
[](const std::tuple<float, float, float> &e) -> decltype(*m.rowwise().begin()) {
/* don't care here */
});

Storing OGRPoint in Boost.Geometry rtree

I'm trying to register the OGR geometry classes with Boost.Geometry to ultimately use them in a Boost.Geometry RTree. To that end, I've followed the tutorial/example in the Boost.Geometry documentation and registered OGRPoint using the BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET macro:
BOOST_GEOMETRY_REGISTER_POINT_2D_GET_SET(
OGRPoint,
double,
boost::geometry::cs::spherical_equatorial<boost::geometry::degree>,
getX,
getY,
setX,
setY)
My simple test driver just creates an RTree with a boost::geometry::model::box Indexable:
typedef bg::model::box<OGRPoint> OGRBox;
typedef std::pair <OGRBox, unsigned> Value;
bgi::rtree<Value, bgi::rstar<16>> rtree;
OGRPoint testP0(12.0, 18.0),
testP1(1.2, 1.8);
rtree.insert(std::make_pair(OGRBox(testP0, testP0), 0));
rtree.insert(std::make_pair(OGRBox(testP1, testP1), 1));
However, I'm stuck with a compile error that boils down to a assertion in Boost:
../../../../include/boost/geometry/index/rtree.hpp:576:398: note: cannot convert 'boost::geometry::index::rtree<Value, Options, IndexableGetter, EqualTo, Allocator>::insert(const Range&)::PASSED_OBJECT_IS_NOT_A_RANGE576::assert_arg<std::pair<boost::geometry::model::box<OGRPoint>, int> >()' (type 'mpl_::failed************ (boost::geometry::index::rtree<Value, Options, IndexableGetter, EqualTo, Allocator>::insert(const Range&) [with Range = std::pair<boost::geometry::model::box<OGRPoint>, int>; Value = std::pair<boost::geometry::model::box<OGRPoint>, unsigned int>; Parameters = boost::geometry::index::rstar<16ul>; IndexableGetter = boost::geometry::index::indexable<std::pair<boost::geometry::model::box<OGRPoint>, unsigned int> >; EqualTo = boost::geometry::index::equal_to<std::pair<boost::geometry::model::box<OGRPoint>, unsigned int> >; Allocator = std::allocator<std::pair<boost::geometry::model::box<OGRPoint>, unsigned int> >]::PASSED_OBJECT_IS_NOT_A_RANGE::************)(std::pair<boost::geometry::model::box<OGRPoint>, int>)') to type 'mpl_::assert<false>::type {aka mpl_::assert<false>}'
BOOST_MPL_ASSERT_MSG((detail::is_range<Range>::value), PASSED_OBJECT_IS_NOT_A_RANGE, (Range));
Is there anything else I need to do, like implementing Boost.Range for boost::geometry::model::box<OGRPoint>?
This problem exist in Boost 1.56 and older.
There are 3 overloads of rtree::insert():
rtree::insert(value_type const&)
rtree::insert(Iter first, Iter last)
rtree::insert(Range const&) // 1.56 and older
In Boost 1.56 and older when an object of a type different than value_type is passed into the insert() member function the rtree treats it as a Range (an object of type adapted to one of the Boost.Range concepts). The error message is produced in compile-time when a parameter is not a Range.
In Boost 1.57 your code should work because the function now recognizes parameters convertible to value_type. Now the 3rd overload is:
rtree::insert(ConvertibleOrRange const&) // 1.57
If you had some suggestions or found a bug don't hesitate to contact the developers on the mailing list or report a bug here.
In that particular case, there was no error with my adapter code. Actually, it was all in the error message to begin with:
typedef std::pair <OGRBox, unsigned> Value;
Note the unsigned.
The error, however states:
std::pair<boost::geometry::model::box<OGRPoint>, int>
Yes, int. The solution is to either use the constructor of std::pair<OGRBox, unsigned> directly, or append u to the number literal, like this:
rtree.insert(std::make_pair(OGRBox(testP0, testP0), 0u));
I cannot belive that this has just caused me three hours of searching. I hope it helps somebody.