Lazy generators: template deduction on the left-hand side
If you are constructing or assigning to a variable from some function template call, the template magic usually occurs on the right-hand side of the expression. For example:
But what if we could get parse
to deduce the type we want to parse from the left-hand side of the construction? I.e. what if we could write this:
This might not seem possible at first glance, since template deduction for parse
can only occur based on the function arguments. However, we can achieve this usage with a technique I call lazy generators.
Instead of parse
doing all of the parsing work, it’s going to return a parser
, which is the lazy generator for our example.
Now the actual parsing work will be done in the implicit conversion template function of parser
:
Now, parser
can implicitly convert to any type with the relevant stream overloads and a default constructor, and this conversion will trigger the parsing.
You can also pass the parser as an argument to functions which expect parsable types (although be careful if doing so multiple times in a function call due to unspecified evaluation order):
If you wanted, you could add some static_assert
s or std::enable_if
tricks to limit the types which your generator converts to:
Of course, as with any technique which relies on implicit conversion, this comes with a ton of caveats and gotchas. For one, you can do some very strange things by saving the parser object and passing it around:
Or you can copy the return value of parse()
:
Or default-construct a parser
:
To prevent the above, you could restrict parser
to only be constructible by parse
, only allow implicit conversion of rvalue parser
s, and delete all of its copy and move operations.
Particularly deviant users can still take a reference to it with auto&& p = parse()
and implicitly convert using std::move(p)
, but they deserve whatever befalls them as a result.
Perhaps you think that this trick is more trouble than its worth for a small example like the above. Mostly I just thought it was a cool trick to have in one’s arsenal, but here are some real-world examples of templated conversion operators, some of which also use lazy generators.
boost::nfp::named_parameter
uses this trick to trace misuses of invalid parameters.boost::python::override
uses it to implicitly convert objects returned from Python.boost::detail::winapi::detail
(quite the mouthful) uses it to allow implicit casting of pointer types for communication with the Windows SDK.boost::initialized_value
uses the templated implicit conversion to provide a generic value initialization method to work around various compiler issues.boost::spirit::hold_any
allows implicit conversion to any type rather than explicit casting in some configurations.boost::multiprecision::number
marks the operatorexplicit
to only allow explicit casting.
I’m sure you could find many more uses for this technique for generic utilities, API adaption, embedded DSLs, etc.
Let me know what you think of this article on twitter @TartanLlama or leave a comment below!