如今在C++中LINQ已经有了众多实现,其中实现度最高,更新最及时,文档最完整的当推LINQ for C++ (cpplinq)。
以下的示例代码使用C++14再现了cpplinq的主要实现技巧。
#include
#include
template
struct from_range
{
// range接口的成员类型
// 其含义为C++容器所包含数据的值类型
using value_type = typename std::iterator_traits::value_type;
// from_range内含三个指向数据的迭代器
// 当前迭代器,其次迭代器,末尾迭代器
TIterator current, upcoming, end;
// from_range的构造器
// 当前迭代器指向容器的开头
// 其次迭代器指向容器的开头
// 末尾迭代器指向容器的末尾
from_range(TIterator begin, TIterator end)
: current(begin), upcoming(begin), end(end) {}
// range接口的成员方法
// 重载>>操作符,该操作符接收一个build对象作为右操作符
// 该操作符将自身传给builder对象的build方法,通过调用该方法实现对range的转换
template
decltype(auto) operator>>(TRangeBuilder builder) const { return builder.build(*this); }
// front方法,返回当前迭代器所指向的数据,即当前数据
decltype(auto) front() const { return *current; }
// next方法,当前数据不存在则返回false,若存在则准备当前数据并返回true
// 具体实现为
// 1. 若其次迭代器与末尾迭代器的值相等,则其次迭代器已经指向容器末尾
// 判断当前数据不存在,返回false
// 2. 若其次迭代器与末尾迭代器的值不等,则其次迭代器尚未指向容器末尾
// 将其次迭代器的值赋予当前迭代器,递增其次迭代器,令其指向下一个数据
// 判断当前数据存在,返回true
bool next() { return upcoming == end ? false : (current = upcoming++, true); }
};
template
struct where_range
{
// range接口的成员类型
// 其含义为转换源range所包含数据的值类型
using value_type = typename TRange::value_type;
// where_range内含两个数据成员
// 转换源range,谓词(过滤条件)
TRange range;
TPredicate predicate;
// where_range的构造器
where_range(TRange range, TPredicate predicate)
: range(range), predicate(predicate) {}
// range接口的成员方法
// 重载>>操作符,该操作符接收一个build对象作为右操作符
// 该操作符将自身传给builder对象的build方法,通过调用该方法实现对range的转换
template
decltype(auto) operator>>(TRangeBuilder builder) const { return builder.build(*this); }
// front方法,返回转换源range的当前数据
decltype(auto) front() const { return range.front(); }
// next方法,当前数据不存在则返回false,若存在则准备当前数据并返回true
// 具体实现为
// 扫描数据源range直至找到符合过滤条件的下一个数据
// 若该数据存在,则当前数据存在,返回true
// 否则当前数据不存在,返回false
bool next(){
while(range.next())
if(predicate(range.front()))
return true;
return false;
}
};
template
struct where_builder
{
// where_builder只包含一个成员:谓词(过滤条件)
TPredicate predicate;
// where_builder的构造器
where_builder(TPredicate predicate) : predicate(predicate) {}
// builder接口的成员方法
// build方法,生成并返回where_range对象
template
auto build(TRange range){
return where_range(range, predicate);
}
};
struct to_vector_builder
{
// builder接口的成员方法
// build方法,生成并返回C++容器
// 具体实现为
// 扫描数据源range
// 将扫描所得到的数据依次插入到C++容器中
template
auto build(TRange range){
std::vector result;
while(range.next())
result.push_back(range.front());
return result;
}
};
// LINQ操作符from,将C++容器转换成from_range对象
template
auto from(TContainer const& container)
{
return from_range(std::begin(container), std::end(container));
}
// LINQ操作符where,生成并返回where_builder对象
template
auto where(TPredicate predicate) { return where_builder(predicate); }
// LINQ操作符to_vector,生成并返回to_vector_builder对象
auto to_vector() { return to_vector_builder(); }
int main()
{
int a[] = {1, 2, 3, 4};
// 找出数组a中所有的偶数,将结果存放到一个vector对象中
auto v = from(a) >> where([](int n){return n % 2 == 0;}) >> to_vector();
for(int i : v) std::cout << i << std::endl;
}
/*
2
4
*/
示例代码总共实现了cpplinq中的三个LINQ操作符from,where以及to_vector。
range和builder是cpplinq的两种基本元素。
range是cpplinq中的数据结构类,内部包含实际数据或者指向实际数据的迭代器。
builder是cpplinq中的行为类,内部的build方法用于实现对range的转换。
典型用例分析
from_range >> where_builder >> to_vector_builder
示例代码分析
auto v = from(a) >> where([](int n){return n % 2 == 0;}) >> to_vector();
cpplinq中所有的range类都实现了range接口。
range接口由一个成员类型和三个成员方法组成。
/// range接口
struct xxx_range
{
// range接口的成员类型
// 其含义为range类的front方法所返回的当前数据的值类型
using value_type = typename ...;
// range接口的成员方法
// 重载>>操作符,该操作符接收一个build对象作为右操作符
// 该操作符将自身传给builder对象的build方法,通过调用该方法实现对range的转换
template
decltype(auto) operator>>(TRangeBuilder builder) const { return builder.build(*this); }
// front方法,返回当前数据
decltype(auto) front() const { return ...; }
// next方法,若当前数据不存在则返回false,若存在则准备当前数据并返回true
bool next() { return ...; }
};
cpplinq中所有的builder类都实现了builder接口。
builder接口只包含一个成员方法。
/// builder接口
struct xxx_builder
{
// builder接口的成员方法
// build方法,转换range的种类或者生成C++容器
template
auto build(TRange range){ return ...; }
};
下面让我们来实现LINQ操作符select。
select操作符生成select_builder对象,该对象负责将range转换为select_range。
template
struct select_range
{
// range接口的成员类型
// 其含义为转换源range所包含数据经过映射后的值类型
using value_type = typename decltype(std::declval()(std::declval()));
// select_range内含两个数据成员
// 转换源range,映射函数selector
TRange range;
TSelector selector;
// select_range的构造器
select_range(TRange range, TSelector selector)
: range(range), selector(selector) {}
// range接口的成员方法
// 重载>>操作符,该操作符接收一个build对象作为右操作符
// 该操作符将自身传给builder对象的build方法,通过调用该方法实现对range的转换
template
decltype(auto) operator>>(TRangeBuilder builder) const { return builder.build(*this); }
// front方法,返回对转换源range的当前数据进行映射后的结果
decltype(auto) front() const { return selector(range.front()); }
// next方法,当前数据不存在则返回false,若存在则准备当前数据并返回true
// 具体实现为
// 调用数据源range的next方法
bool next() { return range.next(); }
};
template
struct select_builder
{
// select_builder只包含一个成员:映射函数selector
TSelector selector;
// select_builder的构造器
select_builder(TSelector selector) : selector(selector) {}
// builder接口的成员方法
// build方法,生成并返回select_range对象
template
auto build(TRange range){
return select_range(range, selector);
}
};
// LINQ操作符select,生成并返回select_builder对象
template
auto select(TSelector selector) { return select_builder(selector); }
改写main函数以便测试select操作符。
int main()
{
int a[] = {1, 2, 3, 4};
// 找出数组a中所有的偶数,乘以2,将结果存放到一个vector对象中
auto v = from(a) >> where([](int n){return n % 2 == 0;}) >> select([](int n){return n * 2;}) >> to_vector();
for(int i : v) std::cout << i << std::endl;
}
/*
4
8
*/