|
1 | | -# CPP-Stream |
2 | | -C++ imitation of Java 8 Stream API |
3 | | - |
4 | | -## Setting up |
5 | | -### Unix: |
6 | | -Variable CMAKE_ARGS is for passing arguments to cmake invocation |
7 | | - |
8 | | -* Simple run : `make` |
9 | | -* Make **DEBUG** build and run tests: `make CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug"` |
10 | | -* Make **RELEASE** build and run tests: `make CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release"` |
11 | | -* Testing coverage (**DEBUG** build only): `make CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug" collect_coverage` |
12 | | - |
13 | | -### Windows: |
14 | | -Use [cmake](https://cmake.org/download/) for generating MSVS solution / smth else : `cd build && cmake.exe ../` |
15 | | - |
16 | | -### Testing coverage (Unix only) |
17 | | -Testing coverage has the next dependencies: |
18 | | -* [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) |
19 | | -* [lcov](https://wiki.documentfoundation.org/Development/Lcov) (install using apt : `apt-get install lcov`) |
20 | | -* [genhtml](https://linux.die.net/man/1/genhtml) |
| 1 | +# cpp-stream |
| 2 | +C++17-compatible imitation of [Java 8 Stream API](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html). |
| 3 | +Elements are processed lazily and with minimum overhead. |
| 4 | + |
| 5 | +## Example |
| 6 | +```cpp |
| 7 | +#include <stream/stream.h> // stream::Stream |
| 8 | +#include <stream/operations.h> // stream::ops::* |
| 9 | +#include <vector> // std::vector |
| 10 | +#include <string> // std::string |
| 11 | +#include <algorithm> // std::transform |
| 12 | +#include <iostream> // std::cout |
| 13 | + |
| 14 | +int main() |
| 15 | +{ |
| 16 | + namespace ops = stream::ops; |
| 17 | + |
| 18 | + const std::vector strings = { |
| 19 | + "dignity", |
| 20 | + "respect", |
| 21 | + "Fred's", |
| 22 | + "sun", |
| 23 | + "color", |
| 24 | + "friends", |
| 25 | + "premium", |
| 26 | + "run", |
| 27 | + "sector", |
| 28 | + "fried", |
| 29 | + "smile", |
| 30 | + "Fritos", |
| 31 | + "for", |
| 32 | + "global", |
| 33 | + "Fridays", |
| 34 | + "broadcast", |
| 35 | + "food" |
| 36 | + }; |
| 37 | + |
| 38 | + // no copying or moving, string is stored via reference |
| 39 | + stream::Stream(strings) |
| 40 | + | ops::filter([](const char *str) { // select only strings started with 'F' or 'f' |
| 41 | + return (str[0] == 'f' || str[0] == 'F'); |
| 42 | + }) // nothing will be evaluated at this point |
| 43 | + | ops::map([](std::string str) { // make every word is in uppercase |
| 44 | + (void)std::transform(str.begin(), str.end(), str.begin(), [](unsigned char ch) { return std::toupper(ch); }); |
| 45 | + return str; |
| 46 | + }) // still nothing will be evaluated |
| 47 | + | ops::print_to(std::cout); // there is terminal operation - causes evaluating everything |
| 48 | + |
| 49 | + return 0; |
| 50 | +} |
| 51 | +``` |
| 52 | +Output: |
| 53 | +> FRED'S FRIENDS FRIED FRITOS FOR FRIDAYS FOOD |
| 54 | +
|
| 55 | +## Supported terminal operations |
| 56 | +* `print_to(ostream)` - prints all elements of the stream to a given output stream `ostream`; |
| 57 | +* `reduce(identityFn, accumulatorFn)` - reduces all elements of the stream to 1 similar to the following pseudo-code: |
| 58 | +```cpp |
| 59 | +stream::Stream s(...); |
| 60 | +auto v = identityFn(s.getNext()); |
| 61 | +while (!s.isEnd()) |
| 62 | + v = accumulatorFn(v, s.getNext()); |
| 63 | +return v; |
| 64 | +``` |
| 65 | +* `reduce(accumulatorFn)` - reduces all elements of the stream to 1 similar to the following pseudo-code: |
| 66 | +```cpp |
| 67 | +stream::Stream s(...); |
| 68 | +auto v = s.getNext(); |
| 69 | +while (!s.isEnd()) |
| 70 | + v = accumulatorFn(v, s.getNext()); |
| 71 | +return v; |
| 72 | +``` |
| 73 | +* `nth(n)` - returns `n`th element of the stream; |
| 74 | +* `to_vector()` - moves all elements of the origin stream to a `std::vector`. |
| 75 | + |
| 76 | +## Supported non terminal operations |
| 77 | +* `skip(n)` - skips n elements of the stream; |
| 78 | +* `get(n)` - takes only n first elements from the stream; |
| 79 | +* `map(mapFn)` - creates a new stream of results of applying the given functor `mapFn` to every element of the given stream; |
| 80 | +* `filter(predicateFn)` - leaves only elements to which applying `predicateFn` functor returns true; |
| 81 | +* `group(n)` - creates a new stream of `std::vector`s with `n` elements of the origin stream. |
| 82 | + |
| 83 | +## Requirements |
| 84 | +* Using the library: |
| 85 | + * C++17-compatible compiler and STL |
| 86 | +* For running tests, you need: |
| 87 | + * [git](https://git-scm.com/downloads) |
| 88 | + * [CMake](https://cmake.org/download/) >= v3.1 |
| 89 | + * Additionally, if you want to measure code coverage, you need: |
| 90 | + * compiler,q |
| 91 | + [gcov](https://en.wikipedia.org/wiki/Gcov) |
| 92 | + [, optional: [lcov](https://wiki.documentfoundation.org/Development/Lcov)] |
| 93 | + compatible with each other |
| 94 | + (for example, gcc 6.3.0, gcov 6.3.0 and lcov 1.13 are compatible) |
| 95 | + * [genhtml](https://linux.die.net/man/1/genhtml) |
| 96 | + * [CMake](https://cmake.org/download/) >= v3.13 |
| 97 | + |
| 98 | +## Build and run tests |
| 99 | +See [Requirements](#requirements) chapter first. |
| 100 | + |
| 101 | +Just use [CMake](https://cmake.org/download/) (run from project root): |
| 102 | +```bash |
| 103 | +# Build |
| 104 | +cd build |
| 105 | +cmake -DCMAKE_BUILD_TYPE=Release .. |
| 106 | +cmake --build . |
| 107 | + |
| 108 | +# Running tests |
| 109 | +ctest -C Release -V |
| 110 | +``` |
| 111 | + |
| 112 | +Note: This project uses [GoogleTest](https://github.com/google/googletest) for testing. |
| 113 | + |
| 114 | +## Code coverage measurement |
| 115 | +See [Requirements](#requirements) chapter first. |
| 116 | + |
| 117 | +CMake scripts provides the next additional variables for setting up the measurement: |
| 118 | +* `COLLECT_CODE_COVERAGE=<LCOV|OTHER|OFF>` - if set to `LCOV` then code coverage target `collect_coverage` will be created. `OTHER` assumes compiling/linking code for measuring via different than lcov instrument ([gcov](https://en.wikipedia.org/wiki/Gcov) for example). `OFF` does nothing and set by default. |
| 119 | +* `GCOV_PATH` - allows to specify path to [gcov](https://en.wikipedia.org/wiki/Gcov) executable. `gcov` by default. |
| 120 | + |
| 121 | +It's recommended to build the code in `Debug` mode for coverage measurement. |
| 122 | + |
| 123 | +Example (run from project root): |
| 124 | +```bash |
| 125 | +# Build |
| 126 | +cd build |
| 127 | +cmake -DCMAKE_BUILD_TYPE=Debug \ |
| 128 | +-DCMAKE_C_COMPILER=gcc-6.3.0 \ |
| 129 | +-DCMAKE_CXX_COMPILER=g++-6.3.0 \ |
| 130 | +-DCOLLECT_CODE_COVERAGE=LCOV \ |
| 131 | +-DGCOV_PATH=gcov-6.3.0 \ |
| 132 | +.. |
| 133 | + |
| 134 | +# Collecting the coverage: |
| 135 | +cmake --build . --target collect_coverage |
| 136 | + |
| 137 | +# Html reports will be generated. Use browser to see it: |
| 138 | +<your_browser_name> ./collect_coverage/index.html & |
| 139 | +``` |
0 commit comments