Boost logo

Geometry :

Subject: Re: [geometry] Run-Time specified geometries
From: Adam Wulkiewicz (adam.wulkiewicz_at_[hidden])
Date: 2014-04-25 07:52:18

Hi Samuel,

Samuel Debionne wrote:
> Hello,
> This thread follows the discussion started here
> about a runtime extension of the library that would deal with geometry
> types that are not available at compile time.
> Short version :
> Algorithms must report compile time errors in a specified way when
> invoked with not supported types (not just rise an MPL assertion) -
> easier said than done.
> Long version :
> So far, some algorithms accept variant parameters, variants that are
> resolved and dispatched to actual implementation and that is a first
> step in this direction.
> Boost.Geometry is heavily using concept checking techniques to make sure
> that any code that does not make sense does not compile (e.g
> intersection of two linestring of different dimensions) and that's the
> beauty of compile time error handling.
> But what if we want to allow every possible OGC types in a variant,
> variant that would be feed from an input of unknown geometry type ?

The best solution would be the implementation of all algorithms for all
Geometries. But this is the theory...

The real question is, should the "errors" which are not really run-time
errors be reported in run-time?
Well I guess they may be seen as library errors.

> typedef boost::variant<
> point_xy_t, point_xyz_t, point_xy_m_t, point_xyz_m_t
> , linestring_xy_t, linestring_xyz_t, linestring_xy_m_t,
> //, who knows what
>> geometries_t;
> Any calls with this type should be "filtered" before instantiating the
> Boost.Geometry algorithms according to the underlying implementation status.
> So far I had some success with simple algorithms filtered with a meta
> function that checks if the dispatcher (the class in the dispatch
> namespace) has not_implemented_tag as a base class and if the dimensions
> are the same for all parameters, like :
> template <typename Geometry1, typename Geometry2>
> struct not_implemented
> {
> typedef typename boost::mpl::or_<
> typename boost::is_base_of<
> nyi::not_implemented_tag,
> dispatch::distance<Geometry1, Geometry2>
> >::type,
> typename boost::mpl::not_<
> typename boost::is_same<
> typename dimension<Geometry1>::type,
> typename dimension<Geometry2>::type
> >::type
> >::type
> >::type
> type;
> };

AFAIU this will raise MPL_ASSERT while trying to compile:

dispatch::distance<Geometry1, Geometry2>

for not supported Geometries?

> and with boost::enable_if to include/exclude a specialization that
> throws a runtime exception when no actual implementation is available
> for the given types :
> template <typename Geometry1, typename Geometry2>
> double operator()(
> const Geometry1& geometry1,
> const Geometry2& geometry2,
> typename boost::disable_if<typename not_implemented<Geometry1,
> Geometry2>::type >::type* = 0) const
> {
> return distance(geometry1, geometry2);
> }
> template <typename Geometry1, typename Geometry2>
> double operator()(
> const Geometry1& geometry1,
> const Geometry2& geometry2,
> typename boost::enable_if<typename not_implemented<Geometry1,
> Geometry2>::type >::type* = 0) const
> {
> throw not_implemented_exception();
> return 0.0;
> }
> On a side note that also requires defining the
> assertion within the not_implemented_error class.

Ah, yes this macro is there for the purpose of disabling MPL_ASSERT for
automatic check of the supported Geometries (basically what you're
doing) for docs. So probably shouldn't be used to disable "normal" error
handling. At least for now.

> Now the questions :
> 1. Testing if not_implemented_tag is a base class of the dispatcher is
> not (yet) reliable. For simple algorithms like distance it works but for
> algorithms that depend on others it won't. Concrete example : intersects
> calls extreme_points. extreme_points is not implemented for anything
> else than rings, polygons and boxes. Yet this limitation is not
> "reported" to the intersects dispatcher.

AFAIU, if an algorithm is not implemented for some pair of Geometries
the dispatch::xxx should be derived from not_implemented<>. And each
dispatched implementation should just use whatever works. So you should
be able to rely on this. Otherwise it should be fixed.

Btw, I couldn't find the extreme_points in intersects implementation.
Where did you find it?

> 2. The dispatcher is an implementation detail (not a public interface)
> so should we rely on it ?

As a user? Probably not. But as a contributor you may propose/do
whatever you like, including using of the internals to implement some
funcionalities, proposing different solutions, extensions, etc.

> 3. The filtering should happend after the variant resolution that means
> rewriting all the variant code in the runtime extension. Unless a hook
> is implemented to augment this code with "runtime filtering".
> 4. Maybe an extension is not a good idea and runtime support should be a
> core functionality of the library. I think so because it seems to
> require an extra requirements : the algorithms must report compile time
> errors in a specified way when invoked with not supported types not just
> rise an MPL assertion.

So assuming we decided that the implementation status should be reported
in run-time for Variants...

Yes, basically dispatch of the algorithm could e.g. take some
NotImplementedPolicy parameter and by default pass it deeper to
not_implemented<> or just use it as a base class. Then the standard
version of the algorithm could use some compile-time policy and
variant-aware version could use run-time one.

The alternative would be to build another dispatching system for the
purpose of run-time libraries errors, a mirror of the compile-time
dispatching, and put it above/on top. This would complicate the
maitnance, a lot of code would be duplicated, etc.

Other ideas?

Bruno is the author of Variant-awarness. I bet he has some thoughts.

> I'm working with the develop branch.
> Sorry for the quite long post,
  It's ok, my posts are longer and nobody wants to read it ;)


Geometry list run by mateusz at