说明
学习自: jscheinyStreams Lazy evaluation in C++ - httpjscheiny.github.ioStreams
Streams is a C++ library that provides lazy evaluation and functional-style transformations on the data, to ease the use of C++ standard library containers and algorithms. Streams support many common functional operations such as map, filter, and reduce, as well as various other useful operations such as various set operations (union, intersection, difference), partial sum, and adjacent difference, as well as many others.
Streams 是一个 C++ 库,它对数据提供延迟评估和函数样式转换,以简化 C++ 标准库容器和算法的使用。流支持许多常见的函数运算,如映射、过滤和减少,以及各种其他有用的运算,如各种集合运算(并集、交集、差)、部分求和和相邻差,以及许多其他运算。
Inspired by jscheiny/Streams
使c++可以像java8 Sreams进行流式操作
例如:
int arr[] = {1,2,3,4,5,6};
int arr_len = sizeof(arr) / sizeof(a[0]);
Stream stream(arr);
int number = stream | filter([](int n){ return n % 2 == 0;}) || count();
- 那么这个库是如何实现的呢?
- 利用现代c++ 比如c++17, c++20,c++23 来对这个库进行升级
下载库
git clone https://github.com/jscheiny/Streams
test
打开test/CMakeLists.txt的如下内容为
externalproject_add(googlemock
PREFIX "${CMAKE_BINARY_DIR}/gtest-1.7.0"
URL "http://googlemock.googlecode.com/files/gmock-1.7.0.zip"
URL_HASH SHA1=f9d9dd882a25f4069ed9ee48e70aff1b53e3c5a5
INSTALL_COMMAND "")
externalproject_get_property(googlemock source_dir binary_dir)
externalproject_add(googlemock
PREFIX "${CMAKE_BINARY_DIR}/gtest-1.7.0"
URL "https://codeload.github.com/MrSapps/gmock-1.7.0/zip/refs/heads/master"
URL_HASH SHA1=bc190f167307e130581046fd4eb805e95b390c8f
INSTALL_COMMAND "")
externalproject_get_property(googlemock source_dir binary_dir)
尝试在archlinux下编译一下,但是无法通过.但没有关系,如果直接使用头文件,可以使用,现在我们来研究整个源代码
#include <iostream>
#include "./source/Stream.h"
int arr[] = {1,2,3,4,5,6};
using namespace stream;
using namespace stream::op;
int main (int argc, char *argv[]) {
int arr_len = sizeof(arr) / sizeof(arr[0]);
MakeStream::from(arr,arr_len) | filter([](int n) { return n % 2 ==0 ;})
| print_to(std::cout," -> ");
// int number = stream | filter([](int n){ return n % 2 == 0;}) || count();
return 0;
}
g++ -g -o t1 t1.cpp
./t1
输出:
2 -> 4 -> 6 ->
整个源代码的结构如下
Streams
├── CMakeLists.txt
├── LICENSE.txt
├── README.md
├── source
│ ├── providers
│ │ ├── AdjacentDifference.h
│ │ ├── AdjacentDistinct.h
│ │ ├── Concatenate.h
│ │ ├── CycledContainer.h
│ │ ├── Difference.h
│ │ ├── Distinct.h
│ │ ├── DropWhile.h
│ │ ├── DynamicGroup.h
│ │ ├── DynamicOverlap.h
│ │ ├── Empty.h
│ │ ├── Filter.h
│ │ ├── FlatMap.h
│ │ ├── Generate.h
│ │ ├── Group.h
│ │ ├── Intersection.h
│ │ ├── Iterate.h
│ │ ├── Iterator.h
│ │ ├── Map.h
│ │ ├── Merge.h
│ │ ├── Overlap.h
│ │ ├── PartialSum.h
│ │ ├── Peek.h
│ │ ├── Providers.h
│ │ ├── Recurrence.h
│ │ ├── Repeat.h
│ │ ├── SetOperation.h
│ │ ├── Singleton.h
│ │ ├── Slice.h
│ │ ├── Sort.h
│ │ ├── Stateful.h
│ │ ├── StreamProvider.h
│ │ ├── StreamProviderIterator.h
│ │ ├── SymmetricDifference.h
│ │ ├── TakeWhile.h
│ │ ├── Union.h
│ │ └── Zip.h
│ ├── reducers
│ │ ├── Histogram.h
│ │ ├── Reducer.h
│ │ └── SummaryStats.h
│ ├── Reducers.h
│ ├── StreamAlgebra.h
│ ├── StreamConversions.h
│ ├── StreamError.h
│ ├── StreamForward.h
│ ├── StreamGenerators.h
│ ├── Stream.h
│ ├── StreamOperations.h
│ ├── StreamOperators.h
│ ├── StreamTerminators.h
│ ├── Utility.h
│ └── UtilityImpl.h
└── test
├── AccessTest.cpp
├── AdjacentDifferenceTest.cpp
├── AdjacentDistinctTest.cpp
├── AlgebraTest.cpp
├── CMakeLists.txt
├── ConcatTest.cpp
├── ConversionTest.cpp
├── CounterTest.cpp
├── CycleTest.cpp
├── EmptyTest.cpp
├── FilterTest.cpp
├── FlatMapTest.cpp
├── ForEachTest.cpp
├── FromTest.cpp
├── GroupTest.cpp
├── MapTest.cpp
├── NumericReductionTest.cpp
├── OverlapTest.cpp
├── PartialSumTest.cpp
├── PeekTest.cpp
├── QuantifierTest.cpp
├── RangeTest.cpp
├── RecurrenceTest.cpp
├── ReduceTest.cpp
├── RepeatTest.cpp
├── SampleTest.cpp
├── SaveTest.cpp
├── SetOperationsTest.cpp
├── SingletonTest.cpp
├── SliceTest.cpp
├── StatefulTest.cpp
├── WhileTest.cpp
└── ZipTest.cpp
5 directories, 87 files
设计一个库,需要先设计出来组件,然后组件互相配合形成新的组件或实现一个功能.
组件其实是对事物与功能的抽象与封装.那么Streams这个库的组件有哪些呢?
provider,源,提供者,可以使用Iterator从中抽取数据,source/providers目录下面有很多源operator,操作器,源与Fucntion的组合,利用function从上一层源中抽取数据- 有状态
- 无状态
terminator,终止器,也是一个操作器,只不过不能再作为源.Stream,容器,一个封装.对源的封装.
我们先研究source/providers下面的代码
这里先不说组件,我们先实现一个"裸"implement,或者说平凡的implement.
我们发现上面的代码的本质就是forwar_list链表,表上的每一个节点都根据上个节点的迭代器获得值.
graph TD
StreamProvider.h -->Iterator.h
StreamProvider.h --> Iterate.h
StreamProvider.h --> Filter.h
先来分析,这个代码的整个执行的流程.
#include <iostream>
#include "./source/Stream.h"
int arr[] = {1,2,3,4,5,6};
using namespace stream;
using namespace stream::op;
int main (int argc, char *argv[]) {
int arr_len = sizeof(arr) / sizeof(arr[0]);
MakeStream::from(arr,arr_len) | filter([](int n) { return n % 2 ==0 ;})
| print_to(std::cout," -> ");
// int number = stream | filter([](int n){ return n % 2 == 0;}) || count();
return 0;
}
MakeStream::from(arr,arr_len)其实是调用了Stream.h的MakeStream类里的一个函数.这个函数又调用了
Stream类的生成函数
template<typename Iterator>
Stream(Iterator begin, Iterator end)
: source_(make_stream_provider<provider::Iterator, T, Iterator>(
begin, end)) {}
可以发现source_就是 一个StreamProviderPtr<T>类型的对象.
StreamProviderPtr<T> source_;
StreamProviderPtr又是由make_stream_provider生成的.
其中:
provider::Iterator是一个Iterator类型的对象.T是一个int类型的对象.Iterator是一个int*类型的对象.
provider::Iterator定义在source/providers/Iterator.h
namespace stream {
namespace provider {
template<typename T, typename Itr>
class Iterator : public StreamProvider<T> {
所以我们选要阅读source/providers里的代码,按下面的顺序阅读
根据map.html里的关系图,StreamProvider.h是一个很基础的类,很多其它的类都需要它
source/providers/StreamProvider.h