Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost custom intervals with interval_map and closed bounds

Tags:

c++

boost

I am trying to use boost::icl::interval_map with a custom interval MyInterval and closed bounds (interval_bounds::static_closed), similarly to interval_set example. This construct is, however, throwing the following error:

---- Map State -----------------
[0,10] - A

prog.exe: /opt/wandbox/boost-1.71.0/gcc-head/include/boost/icl/interval_base_map.hpp:557: boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::gap_insert(boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator, const interval_type&, const codomain_type&) [with Combiner = boost::icl::inplace_plus<std::__cxx11::basic_string<char> >; SubType = boost::icl::interval_map<int, std::__cxx11::basic_string<char>, boost::icl::partial_absorber, std::less, boost::icl::inplace_plus, boost::icl::inter_section, MyInterval>; DomainT = int; CodomainT = std::__cxx11::basic_string<char>; Traits = boost::icl::partial_absorber; Compare = std::less; Combine = boost::icl::inplace_plus; Section = boost::icl::inter_section; Interval = MyInterval; Alloc = std::allocator; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::iterator = std::_Rb_tree<MyInterval, std::pair<const MyInterval, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const MyInterval, std::__cxx11::basic_string<char> > >, boost::icl::exclusive_less_than<MyInterval>, std::allocator<std::pair<const MyInterval, std::__cxx11::basic_string<char> > > >::iterator; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::interval_type = MyInterval; boost::icl::interval_base_map<SubType, DomainT, CodomainT, Traits, Compare, Combine, Section, Interval, Alloc>::codomain_type = std::__cxx11::basic_string<char>]: Assertion `this->_map.find(inter_val) == this->_map.end()' failed.

I have noticed that either (1) using another interval_bounds type, e.g. static_open, or (2) using default interval_type, i.e.interval<int>::closed(), works just fine. Using MyInterval and static_closed bounds combination seems to be the problem. What configuration am I missing or what have I done incorrectly?

Code: below or Wandbox.

Boost: 1.71

GCC: 9.2.0.

MyInterval.hxx

#ifndef MY_INTERVAL_HXX
#define MY_INTERVAL_HXX

#include <iostream>
#include <boost/icl/interval_map.hpp>

using namespace std;
using namespace boost::icl;

class MyInterval
{
public:
  MyInterval(): _first(), _past(){}
  MyInterval(int lo, int up): _first(lo), _past(up){}
  int first()const{ return _first; }
  int past ()const{ return _past; }
private:
  int _first, _past;
};


namespace boost { namespace icl
{
  template<>
  struct interval_traits<MyInterval>
  {
    typedef MyInterval           interval_type;
    typedef int                  domain_type;
    typedef std::less<int>       domain_compare;

    static interval_type construct(const domain_type &lo, const domain_type &up)
    { return interval_type(lo, up); }

    static domain_type lower(const interval_type &inter_val) { return inter_val.first(); }
    static domain_type upper(const interval_type &inter_val) { return inter_val.past(); }
  };

  template<>
  struct interval_bound_type<MyInterval>
  {
    typedef interval_bound_type type;
    BOOST_STATIC_CONSTANT(bound_type, value = interval_bounds::static_closed);
  };
}} // namespace boost icl

#endif

Main.cxx

#include <iostream>
#include <cstdlib>
#include <string>
#include <boost/icl/interval_map.hpp>
#include "MyInterval.hxx"

using namespace boost::icl;

int main()
{
    interval_map <int
      , std::string
      , partial_absorber
      , std::less // ICL_COMPARE_INSTANCE(ICL_COMPARE_DEFAULT, int),
      , inplace_plus // ICL_COMBINE_INSTANCE(inplace_plus, int),
      , inter_section // ICL_SECTION_INSTANCE(inter_section, int),
      , MyInterval
      > imap;

    std::string A("A");
    std::string B("B");
    std::string C("C");
    auto Ai = MyInterval(0,10);
    //auto Ai = interval<int>::closed(0,10);
    auto Bi = MyInterval(5,15);
    //auto Bi = interval<int>::closed(5,15);
    auto Ci = MyInterval(7,12);
    //auto Ci = interval<int>::closed(7,12);

    imap += std::make_pair(Ai, A);
    std::cout << "---- Map State -----------------" << std::endl;
    for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
    std::cout << std::endl;

    imap += std::make_pair(Bi, B);
    std::cout << "---- Map State -----------------" << std::endl;
    for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
    std::cout << std::endl;

    imap += std::make_pair(Ci, C);
    std::cout << "---- Map State -----------------" << std::endl;
    for (const auto& val : imap) { std::cout << val.first << " - " << val.second << std::endl; }
    std::cout << std::endl;

    return 0;
}

Expected Output

---- Map State -----------------
[0,10] - A

---- Map State -----------------
[0,5) - A
[5,10] - AB
(10,15] - B

---- Map State -----------------
[0,5) - A
[5,7) - AB
[7,10] - ABC
(10,12] - BC
(12,15] - B
like image 657
delirium Avatar asked Apr 23 '20 19:04

delirium


1 Answers

This is the same problem a friend of mine had once.

The problem is that the default constructor of your custom class is not right. I'm not sure if the boost documentation says this, but it must produce an invalid range. In your case, the default constructor is producing a range [0, 0].

Basically, the way the library checks if a range is valid is based on the type of bounds:

  • If it is static_open, for instance the function that checks if the interval is invalid is equivalent to upper() <= lower(). That is why it works in your code: 0 <= 0 -> true.
  • For the static_closed, the function that checks if the interval is invalid is equivalent to upper() < lower().

In order for it to work, you need to change the default constructor to produce something where upper() < lower().

TL;DR

Change the default constructor to something like:

MyInterval() : _first(0), _past(-1) {}
like image 179
mhbackes Avatar answered Nov 13 '22 08:11

mhbackes