4

I was wondering if there was a way to loop through the dimensions of a boost point model. I am trying to create a function to do calculations on two custom points, with a definable number of dimensions. In other words the number of dimensions of each point will match, however they will not be a constant value. I want to do the same operations on each dimension, so I need to do a loop in order to achieve this.

An example of what I want I want to do would be:

for(std::size_t dim = 0; dim < D; dim++){
    CoordinateType d = get<dim>();

    //do stuff to d

    set<dim>(d);

}

I know this would not work because d is not a compile-time constant.

Thanks!

Zack Frost
  • 155
  • 1
  • 12

2 Answers2

5

As an alternative approach I thought you should be able to adapt the Boost Geometry point model as a Fusion sequence.

Live On Coliru

#include <iostream>

namespace bg  = boost::geometry;
namespace fus = boost::fusion;

int main() {
    bg::model::point<double, 7, bg::cs::cartesian> p1;
    // set some nice values
    p1.set<0>(7);  p1.set<1>(14); p1.set<2>(21); p1.set<3>(28);
    p1.set<4>(35); p1.set<5>(42); p1.set<6>(49);

    fus::for_each(fus::as_vector(p1), [](double x) { std::cout << x << ' '; });
}

Prints

7 14 21 28 35 42 49 

This is pretty versatile (and will give you many more algorithms than just for_each). In the sample I've not gone all the way so you can actually say for_each(p1, f) instead of for_each(as_vector(p1), f) but you know... the proverbial exercise for the reader.

There's a bit of extension "glue" code involved here. I simply followed the documentation here

See the full listing here:

Live On Coliru

#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/geometries/point.hpp>

#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/as_vector.hpp>

namespace bg_to_fusion {
    using namespace boost;

    struct bg_point_tag;
    struct example_struct_iterator_tag;

    template<typename Point, int Pos>
    struct point_iterator
        : fusion::iterator_base<point_iterator<Point, Pos> >
    {
        BOOST_STATIC_ASSERT(Pos >=0 && Pos <geometry::traits::dimension<typename remove_cv<Point>::type>::value);

        typedef Point point_type;
        typedef mpl::int_<Pos> index;
        //typedef fusion::random_access_traversal_tag category;
        typedef fusion::forward_traversal_tag category;

        point_iterator(Point& p) : point_(p) {}
        Point& point_;
    };
}

namespace boost { namespace fusion {

    // tag dispatch
    namespace traits {

        template <typename T, size_t dims, typename cs>
        struct tag_of<geometry::model::point<T, dims, cs> > {
            typedef bg_to_fusion::bg_point_tag type;
        };

        template <typename Point, int Pos>
        struct tag_of<bg_to_fusion::point_iterator<Point, Pos> > {
            typedef bg_to_fusion::example_struct_iterator_tag type;
        };

    }

    namespace extension {

        //////////////////////////////////////////////////////
        // Point extension implementations
        template<>
        struct is_sequence_impl<bg_to_fusion::bg_point_tag>
        {
            template<typename T>
                struct apply : mpl::true_ {};
        };

        template <>
        struct size_impl<bg_to_fusion::bg_point_tag> {
            template <typename Point>
            struct apply : mpl::integral_c<size_t, geometry::traits::dimension<typename remove_cv<Point>::type>::value> { };
        };

        // begin
        template<>
        struct begin_impl<bg_to_fusion::bg_point_tag>
        {
            template<typename Point> 
            struct apply 
            {
                typedef typename bg_to_fusion::point_iterator<Point, 0> type;

                static type
                call(Point& p) 
                {
                    return type(p);
                }
            };
        };

        // end
        template<>
        struct end_impl<bg_to_fusion::bg_point_tag>
        {
            template<typename Point> struct apply {
                typedef typename bg_to_fusion::point_iterator<Point, geometry::traits::dimension<Point>::value> type;

                static type call(Point& p) {
                    return type(p);
                }
            };
        };

        ////////////////////////
        // Iterator extension implementations

        // value_of
        template <>
        struct value_of_impl<bg_to_fusion::example_struct_iterator_tag> {
            template<typename Iterator> struct apply;

            template<typename Point, int Pos> 
            struct apply<bg_to_fusion::point_iterator<Point, Pos> > {
                typedef typename geometry::traits::coordinate_type<typename remove_cv<Point>::type>::type type;
            };
        };

        // deref
        template<>
        struct deref_impl<bg_to_fusion::example_struct_iterator_tag>
        {
            template<typename Iterator>
            struct apply;

            template<typename Point, int Pos>
            struct apply<bg_to_fusion::point_iterator<Point, Pos> >
            {
                typedef typename geometry::traits::coordinate_type<typename remove_cv<Point>::type>::type coordinate_type;

                //typedef typename mpl::if_<is_const<Point>, coordinate_type const&, coordinate_type&>::type type;
                typedef coordinate_type type;

                static type
                call(bg_to_fusion::point_iterator<Point, Pos> const& it) {
                    return it.point_.template get<Pos>();
                }
            };
        };

        // next
        template<>
            struct next_impl<bg_to_fusion::example_struct_iterator_tag> {
                template<typename Iterator> struct apply
                {
                    typedef typename Iterator::point_type point_type;
                    typedef typename Iterator::index    index;
                    typedef typename bg_to_fusion::point_iterator<point_type, index::value + 1> type;

                    static type
                        call(Iterator const& i) {
                            return type(i.point_);
                        }
                };
            };
    }

} }
sehe
  • 374,641
  • 47
  • 450
  • 633
  • nice solution as it gives you access to all kind of boost fusion algorithms. +1 – linuxfever May 12 '15 at 05:16
  • If using a `model::point` is not absolutely necessary and you can use a custom struct as your point, I think [this approach](http://coliru.stacked-crooked.com/a/e690c73c193fa9e9) is also nice. – llonesmiz May 12 '15 at 06:09
  • @cv_and_he yup that was my first thought. I guess I could have commented that on the question :) This is mostly doing the tedious work of adapting manually (with the benefit of partially specializing for all coordinate types, dimensions and coordinate systems) – sehe May 12 '15 at 07:01
2

I am not very familiar with boost geometry but it seems you have to iterate through the coordinates at compile time. The way to do this is by creating a recursion. The class apply below will perform such recursion and call a functor on each coordinate of the point.

All you have to do is to write your own functor and overload its operator() in case you want to specialise behaviour for certain coordinates. The functor in the example below simply prints the coordinates and is specialised when reading the 3rd coordinate.

#include <boost/geometry.hpp>
#include <iostream>
#include <type_traits>


namespace bg = boost::geometry;

template <int I>
using int_ = std::integral_constant<int, I>;

//recursive call that iterates the point and calls F on its coordinate
template <class Point, class F, std::size_t I = 0>
struct apply {

    static void call(Point& point, F& f) {
        f(point, int_<I>());
        apply<Point, F, I+1>::call(point, f);
    }
};

//specialisation to end the recursion
template <class CT, std::size_t DIM, class S, template <class, std::size_t, class> class Point, class F>
struct apply<Point<CT, DIM, S>, F, DIM> {

    static void call(Point<CT, DIM, S>& point, F& f){}
};

//interface for calling the function
template <class Point, class F>
void apply_functor(Point& point, F& f) {
    apply<Point, F>::call(point, f);
}

//example functor
template <class Point>
struct functor {

    template <class Index>
    void operator()(Point& point, Index I) {
        std::cout << "I am coordinate " << Index::value << " and my value is " << bg::get<Index::value>(point) << std::endl;
    }

//    used for overloading when reading the 3rd coordinate
    void operator()(Point& point, int_<2>) {
        std::cout << "I am coordinate " << 2 << " and I am specialised with value " << bg::get<2>(point) << std::endl;
    }
};


//3-dimensional point type
using point_type = bg::model::point<double, 3, bg::cs::cartesian>;

int main(int argc, char** argv) {
    point_type point(1,2,3);
    functor<point_type> f;
    apply_functor(point, f);

    return 0;
}
linuxfever
  • 3,763
  • 2
  • 19
  • 43
  • Yeah. The good old handy-work seems nicer here. I do have a feeling that it can be even simpler with judicious use of `make_array` or `make_tuple` and variadics. +1 – sehe May 11 '15 at 22:37