An open typeswitch using lambdas

Hi,
I would like to get feedback on a small typeswitch library I've been
working on for some time now. The library is designed to dispatch code
(lambdas) based on the actual value of boost::any, boost::variant, and also
general polymorphic hierarchies. An example is in order.
template <class Poly>
void test_poly(Poly& p)
{
match(p)(
[](int i) { std::cout << "int = " << i << "\n"; },
[](double d) { std::cout << "double = " << d << "\n"; },
[](const std::string &s) { std::cout << "string = " << s << "\n"; },
[](std::ostream &o) { std::cout << "found ostream" << "\n"; }
);
}
int main(void)
{
std::ios_base &stream = std::cout;
boost::any any(std::string("C++ Truths"));
boost::variant

29.11.2013 22:31, Sumant Tambe:
template <class Poly> void test_poly(Poly& p) { match(p)( [](int i) { std::cout << "int = " << i << "\n"; }, [](double d) { std::cout << "double = " << d << "\n"; }, [](const std::string &s) { std::cout << "string = " << s << "\n"; }, [](std::ostream &o) { std::cout << "found ostream" << "\n"; } ); }
Just for your information, there is Boost.Functional/OverloadedFunction library - http://www.boost.org/doc/libs/1_55_0/libs/functional/overloaded_function/doc... It has make_overloaded_function template function http://www.boost.org/doc/libs/1_55_0/libs/functional/overloaded_function/doc... - which is somewhat similar (not sure if it provides required typedef for result_type). -- Evgeny Panasyuk

On 29/11/2013 19:31, Sumant Tambe wrote:
The implementation of the library is a little over 250 lines. Check it out here: http://coliru.stacked-crooked.com/a/72597a93b12916d4. clang is the only compiler I've been able to compile it with.
Doing the kind of thing you're doing should be easy to do with any C++
compiler.
You're apparently over-complicating things.
That stuff can simply be done with
template

Well, gcc 4.9 has a name lookup bug (
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58820) and Visual Studio 2013
Nov CTP chokes on lambda multiple inheritance.
Also, I'm not sure what you are suggesting with the struct F example.
Simply bringing the base class's operator () in the current scope does not
provide "typeswitch" capability.
On 29 November 2013 14:33, Mathias Gaunard
On 29/11/2013 19:31, Sumant Tambe wrote:
The implementation of the library is a little over 250 lines. Check it out
here: http://coliru.stacked-crooked.com/a/72597a93b12916d4. clang is the only compiler I've been able to compile it with.
Doing the kind of thing you're doing should be easy to do with any C++ compiler. You're apparently over-complicating things.
That stuff can simply be done with
template
struct F : F1, F2 { using F1::operator(); using F2::operator(); }; This approach also has the advantage that it doesn't require monomorphic lambdas, it can build an overload of arbitrary function objects.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost

On Fri, Nov 29, 2013 at 11:47 PM, Sumant Tambe
Well, gcc 4.9 has a name lookup bug ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58820) and Visual Studio 2013 Nov CTP chokes on lambda multiple inheritance.
Also, I'm not sure what you are suggesting with the struct F example. Simply bringing the base class's operator () in the current scope does not provide "typeswitch" capability.
In other words, you pull in the operator () from each of the function objects and then just invoke the composite object's set of overloaded operator() functions. You get exactly the behavior of overload resolution of a qualified name because that's exactly what's going on. It also should not be at all slower than simply invoking a function. This will work fine for things like variant visitors right out of the box, too, assuming you inherit from boost::static_visitor. If you want to support type-erased objects, it's not quite that simple, though I personally am not very interested in those capabilities. At the very least, it should be possible to make the simple, overload-based one without the overhead of a more dynamic approach. The only issue I immediately see with the overload implementation is that it will require special handling if someone passes in a function pointer, which is a perfectly valid function object so you'd expect it to just work as a user of match. That said, support for function pointers would still be very easy to add in, it's just something that shouldn't be forgotten. Anyway, regardless of the implementation, I do think that this is a worthwhile tool. Make it inherit from boost::static_visitor and I'll start using as soon as I can. It should be possible to automatically deduce the return type in some cases, especially with the help of a type list of possible arguments (a variant type or instance would also be an option), falling back on an optional, user-specified return type via an explicit template argument. This would be extremely useful for creating boost::variant visitors right at the apply_visitor call-site. It might even make sense to have something like this that does the visitation right when the object is formed, given that simply using it once as an argument to apply_visitor seems like a common use-case. -- -Matt Calabrese

On 30/11/2013 09:37, Matt Calabrese wrote:
On Fri, Nov 29, 2013 at 11:47 PM, Sumant Tambe
wrote: Well, gcc 4.9 has a name lookup bug ( http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58820)
The bug is probably just worked around by using "using operator()". When I was experimenting with that kind of thing, it worked just fine.
and Visual Studio 2013 Nov CTP chokes on lambda multiple inheritance.
You could wrap lambdas into proper function objects to work around this.
If you want to support type-erased objects, it's not quite that simple, though I personally am not very interested in those capabilities. At the very least, it should be possible to make the simple, overload-based one without the overhead of a more dynamic approach.
The "open" in "open typeswitch" means that it doesn't require a closed set of types. The open aspect is difficult to do properly. The issues to me with the kind of implementation used here is that it has too big a requirement on the function objects to handle the different cases, doesn't deal well with conversions, cannot use generic code etc. If we had stuff like virtual templates it would be possible to treat open and closed type switch the same way, but emulating virtual templates by hand is a bit clunky.
Anyway, regardless of the implementation, I do think that this is a worthwhile tool. Make it inherit from boost::static_visitor and I'll start using as soon as I can.
AFAIK variant doesn't require that your visitors inherit from static_visitor, just that they define the result_type typedef.

On Sat, Nov 30, 2013 at 5:11 AM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
AFAIK variant doesn't require that your visitors inherit from
static_visitor, just that they define the result_type typedef.
Well, whatever the current requirement is specified as, it would be nice if this utility met that requirement. Being able to easily write variant visitors at the call-site is very useful. Getting a reasonable, deduced return type should be easy with decltype and common_type (really, apply_visitor should probably do this if it hasn't been updated to do so already, using the explicit result_type instead only if it is specified, and allowing the return type to be an explicit template argument to apply_visitor). Anything that makes variants easier to use is a great addition to boost, IMO. -- -Matt Calabrese

Great feedback so far. Thanks! If I know what I'm talking about then, typeswitch != overload resolution typeswitch != static_visitor When a programmer uses overload resolution, it's more like: "Mr. Compiler, here are a bunch of functions named identically. Select the best one based on the parameter I have here. Feel free to do some reasonable conversions along the way," When a programmer uses a typeswitch, it's more like: "Hey typeswitch, here are a bunch of typecases I want you to match exactly to the parameter. If none of them match, don't bother calling any. So, no conversions, please. If you see a default typecase (i.e., a lambda wrapped in an "otherwise" functor) use that one if you don't find an exact match". Example: void func(std::string); void func(double); int var; func(var) // calls func(double) as per overload resolution. on the other hand, match(var)( [](std::string) { std::cout << "Ok string\n"; }, [](double) { std::cout << "Ok double\n"; }, otherwise([](auto something){ std::cout << "don't know\n"; }) ); prints "don't know". This is expected and desirable when I have my typeswitch hat on. Some may wonder if this is a right design choice. Well, that's the point, it is a choice. If you need good old overload resolution, the language has it and you have an option of using boost.OverloadFunction library when needed. (pretty cool library btw). I see no reason to duplicate that. typeswitch is also quite different from the static_visitor because typeswitch does not statically check if all the possible cases are handled. boost.variant static_visitor does that. That's great when you want it. But in the case of boost.any (also poco::dynamic_any), no compile-time solution is possible. In the above match example, I don't think twice if var were a boost.any. I would naturally expect otherwise clause being called because the any has an integer and the specific typecases don't match an int. The typeswitch library is also applicable to polymorphic class hierarchies. The tests (dynamic_cast) must be performed at run-time in that case.
The only issue I immediately see with the overload implementation is that it will require special handling if someone passes in a function pointer, which is a perfectly valid function object so you'd expect it to just work as a user of match. That said, support for function pointers would still be very easy to add in, it's just something that shouldn't be forgotten.
As this is not an yet another overload implementation, there is probably less need to support function pointers. That support is already available in boost.function_overload. Furthermore, you could simply wrap a function pointer in lambda typecase.
.... This would be extremely useful for creating boost::variant visitors right at the apply_visitor call-site.
Certainly. Such capability belongs in boost.variant where all the visit cases will be enforced statically.
The bug is probably just worked around by using "using operator()". When I was experimenting with that kind of thing, it worked just fine.
Right. Any idea how to use the using syntax with variadic parameters. I
tried the following which did not work.
template
You could wrap lambdas into proper function objects to work around this.
That's what the "otherwise" clause does in my implementation of match.
... cannot use generic code etc
Again, the "otherwise" clause is a good place to use a generic lambda if that's what you meant.
Getting a reasonable, deduced return type should be easy with decltype and common_type (really, apply_visitor should probably do this if it hasn't been updated to do so already, using the explicit result_type instead only if it is specified, and allowing the return type to be an explicit template argument to apply_visitor).
Interesting! Let me think more on that a bit. As of now, anything that the
typecase lambdas return is simply tossed away.
R/ Sumant
On 30 November 2013 09:46, Matt Calabrese
On Sat, Nov 30, 2013 at 5:11 AM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
AFAIK variant doesn't require that your visitors inherit from
static_visitor, just that they define the result_type typedef.
Well, whatever the current requirement is specified as, it would be nice if this utility met that requirement. Being able to easily write variant visitors at the call-site is very useful. Getting a reasonable, deduced return type should be easy with decltype and common_type (really, apply_visitor should probably do this if it hasn't been updated to do so already, using the explicit result_type instead only if it is specified, and allowing the return type to be an explicit template argument to apply_visitor). Anything that makes variants easier to use is a great addition to boost, IMO.
-- -Matt Calabrese
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (4)
-
Evgeny Panasyuk
-
Mathias Gaunard
-
Matt Calabrese
-
Sumant Tambe