-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: Improve Variant support (run-time exception, lazy_not_implemented) #290
Conversation
For variants throw an exception depending on the supported combinations instead of a compile-time assert failure.
…tion. Specialize lazy_not_implemented for void Result. Add throw() exception specification to not_implemented_exception::which().
This is awesome! I have been struggling on this problem for a while now, trying to find the less intrusive way to implement that functionality. I like your solution: AFAIU adapting an algorithm means adding an extra step at the variant resolve stage to check if the algo is implemented using is_not_implemented and the MetaPolicy. Getting this MetaPolicy right may be a bit tricky though since not every "not implement feature" are "reported" to the dispatch class. IOW, checking if
It could be handy to have a few predefined metapolicy (e.g. one that would check that the dimension and coordinate systems of the geometries are the same) and compose them to build the May be some tests with Thank you for this excellent PR. |
Indeed you're right. Thanks for bringing those points up!
As for the I'm not sure if we should allow to store in a Variant type Geometries using different dimensions or coordinate systems. The library in general dissallows such usage. So yes, we could improve the support even more. This is only the first step. Before moving forward I'd like to get feedback from others if they agree that it's a good direction. |
This is definitely a good start and the potential issues mentioned above should not stop moving forward!
My opinion is that variants of different dimensions or coordinate systems should be allowed. If not, the user has to handle this use case (which is quite common IMHO) upstream and that may be a bit cumbersome: that would probably mean re-implementing the resolve variant stage. Unless the MetaPolicy is exposed in a public interface of the algorithm and could be customized by the user: template<typename Geometry1, typename Geometry2, typename MetaPolicy = resolve_variant::within_metapolicy>
inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2) This would give the maximum flexibility at the expense of an additional (defaulted) template parameter. |
Conflicts: test/algorithms/relational_operations/within/Jamfile.v2
So what are we going to do with this PR? |
@brunolalande What is your opinion on this? Is it okay or would you have liked to do something else? |
Hi, I never really had the time to dig for a solution to this problem, and this approach looks promising so I'd give it a go. I would just recommend to try it on a more complicated algorithm just to check we are not going to come across any showstopper that would force us to change directions. Regarding the situation where algorithm A falls back to B, it has always been my feeling that A's dispatch class should systematically inherit from B's dispatch class. Doing this consistently means we can rely on it in the situation described in this conversation, and many others (I tried to generalize it when I implemented support_status, as it was critical to have correct results). The problem is how to enforce it - technically, inheriting from a dispatch class is not needed to call its apply function, so it's easy to miss. We could reimplement dispatch classes to make this mandatory, by making the apply function protected or something... Regarding mixing several dimensions/CS in one variant, I'm fundamentally against it but I'd like examples of when this would be needed. Samuel you're saying it's "quite common", what situations do you have in mind? Regards |
Well, "it's quite common" is always easier said than proved! Anyway, here is my use case: I'm building a spatial extension for sqlite, in the same vein than spatialite but with Boost.Geomtry instead of GEOS. See a minimalist implementation of ST_Within for example: void fnct_within(
sqlite3_context *context,
int argc,
sqlite3_value **argv)
{
GEOMLITE_ASSERT(argc == 2);
GEOMLITE_CHECK_ARGS_TYPE(argv[0], SQLITE_BLOB);
GEOMLITE_CHECK_ARGS_TYPE(argv[1], SQLITE_BLOB);
gis::cgeometries_t geom;
gis::read_blob(argv[0], geom1);
gis::read_blob(argv[1], geom2);
try
{
bool b = bg::within(geom1, geom2);
sqlite3_result_int(context, b ? 1 : 0);
}
catch(bg::not_implemented_exception&)
{
sqlite3_result_error_code(context, SQLITE_MISMATCH);
}
}
Supposing that mixed dimensions is not supported, I would need to make sure that Within the framework of this PR, it would be helpfull to let the user override the MetaPolicy. In return, why are you fundamentally against it ? |
Perfect!!! That is good news. |
Hmm sorry, it looks like I missed one important word in my reply, I meant "I'm NOT fundamentally against it" :-) Thanks for the example, I was too much focused on situations where you would indeed want to have geometries of different dimensions interact, while the problem is more that you don't know at runtime what will be the dimension. I agree that such cases will arise as soon as you deal with dynamic input. Regards |
@awulkiew I have been playing a bit with your PR. With MSVC 12 all is fine but with clang 3.6, I get a compile time error reported by the default strategy. My variant is made of @barendgehrels Thank you for your support! It's a side project though, not a full time commitment, so it's a slow work in progress. The results are rather promising in terms of performance. |
@sdebionne I haven't tested with clang yet. It doesn't surprise me that strategies can fail because I haven't touch them yet, so the old code is used for strategies and as you noticed an MPL_ASSERT is checked there. As I mentioned above the strategies should also be altered in a similar way how the dispatched algorithm were. Sorry that I left this PR hanging but I don't have time right now to play with it. I'm planning to get back to it for a while, but if you'd like to play with it feel free to do it. I wouldn't have anything against closing this PR if you came up with a more mature solution. I'm also not surprised that something wich should fail doesn't fail with MSVC. We must have in mind that the code should fail with all compilers in a similar way. So the fact that it doesn't is worrying. But does it fail if you pass non-supported combination, e.g. |
@awulkiew Here is what if found that could explain the difference of behavior between the two compilers: Clang is fairly strictly conforming to the two-phase name lookup. However, MSVC has a template parsing model that delays nearly every lookup to instantiation time, which is part of phase 2. This is probably why the code would compile with MSVC (which is non-conforming bu compiling) and not in clang. There is a pretty neat article on the clang blog: The Dreaded Two-Phase Name Lookup
Yes, it does fail as expected. In a way, MSVC behavior is more user-friendly, as it does no instantiate template that will never been used (the resolve variant stage would throw
Strategies are not currently using |
@sdebionne Regarding failing with Clang, which algorithms, geometries and coordinate systems have you tested? |
@awulkiew From the top of my head, I was trying to implement |
Has this been published in any repository? @awulkiew What is the status of this PR? |
@awulkiew this is quite old ;-) |
@barendgehrels sure, I don't mind. |
This PR adds 2 new utilities which could be used to improve the Variant support:
apply()
member functions instead of a struct body. It allows to check if the algorithm is not implemented at compile-time without the static assertion failure. A side effect is the generation of lesser number of errors v.s. not_implemented.MetaPolicy
which must define astruct apply<G1, G2>
defining a value indicating if e.g. an algorithm is implemented. It's specialized forboost::variant
and "returns" true only if all of the combinations of Variants elements are not defined.Both of those tools allows to check in compile-time if an algorithm should be able to work for specific Variant types if only the user stored supported Geometries in those Variants. So compile-time errors works like before. In the case of Variants, for supported combinations the code calling the algorithm is generated and for not supported combinations the code throwing
bg::not_implemented_exception
is generated. This allows to use an algorithm like this: