3 ms·
I tried implementing transducers in MSVC about a year ago. I got pretty far in implement a lot of the basic set of transducers one would use in development. How
by AndrewGaspar 11y ago
I tried implementing transducers in MSVC about a year ago. I got pretty far in implement a lot of the basic set of transducers one would use in development. However, I ultimately abandoned it because they weren't really a great fit with C++. You could make it all work, but the meta template code is extremely difficult to reason about, and MSVC specifically had a lot of issues processing the code (pretty quickly bumped up against MSVC's type name size restriction!). There are also a lot of issues to think about regarding lifetime semantics - figuring out the right behavior was pretty difficult, and debugging issues with it pretty frustrating, especially when the code was optimized.
The pros were that, at least for my limited testing, performance was essentially equivalent, for optimized code, between the transducer implementation and the equivalent hand rolled code (e.g., doing a map+filter transducer vs. a foreach with an if), and that using transducers offered further optimizations if you applied them completely up and down the stack, by allowing you to, say, early terminate a sequence that would otherwise have been done in entirety behind some opaque function call. I don't really consider code readability to be a benefit here since transducers are, somewhat inherently, a heady subject.
You can see my work here: https://github.com/AndrewGaspar/transducers.hpp https://github.com/AndrewGaspar/transducers.hpp
I haven't done a formal write-up or anything on it, but check out the tests if you want to see what consuming them would kind of look like.
EDIT: One of the things I'm proud of was rigging it, without need of dynamic typing, such that a transducer could produce heterogeneous types. Obviously your sink would need to be capable of accepting all these different types being produced by the transducible process, but it allowed for some interesting integration with <iostream> - say, creating a transducer that inserts comma's between integers for output (see https://github.com/AndrewGaspar/transducers.hpp/blob/master/include/transducers/interjecting.hpp https://github.com/AndrewGaspar/transducers.hpp/blob/master/... and https://github.com/AndrewGaspar/transducers.hpp/blob/master/unit_tests/interjecting.cpp https://github.com/AndrewGaspar/transducers.hpp/blob/master/...).
EDIT 2: I notice that early termination was not shown in this blog post. I implemented this by essentially introducing a known wrapping type (https://github.com/AndrewGaspar/transducers.hpp/blob/master/include/transducers/escape_hatch.hpp https://github.com/AndrewGaspar/transducers.hpp/blob/master/..., sorry I know it's a bad name). The trick with this was I only wanted the returned values of a transducible process to be wrapped by this struct (since it has the overhead of one bool value, and a check to actually determine if it could early terminate) if a single transducer in the chain can early terminate. It also made each individual transducer slightly more complicated to implement, unfortunately.