It all started with the fact that I at the institute and after graduation wrote code in C ++ and did not know the troubles. But then one day I had to write code under .NET in C #. At first a little spat, but then nothing - was involved. I saw the advantageous differences from C ++: security, rigor, etc. Also I could not ignore LINQ when working with collections ...
Introduction to the issue
But I appreciated the beauty of LINQ when it came time to return to C ++. It was a bit unusual to write in C ++, after a six-month hiatus. Nothing foreshadowed troubles, when suddenly I needed to calculate the sum of the elements in a vector, more specifically the sum of the fields of the elements of a vector. In C #, this would be solved like this:
')
int sum = candles.Sum(c => c.ClosePrice);
But in C ++ it came out:
int sum = 0; for(int i = 0; i < candles.size(); i++) sum += candles[i].ClosePrice;
And if to rewrite on iterators:
int sum = 0; for(auto it = candles.begin(); it != candles.end(); ++it) sum += it->ClosePrice;
Qt makes things a little easier, but not too much:
int sum = 0; foreach(Candle candle, candles) sum += candle.ClosePrice;
Also, the new C ++ standard standard promises us a simplification
but Visual Studio 2010
unfortunately does not support this feature , but
how ... (thanks to
Damaskus ):
int sum = 0; for (Candle candle : vector) sum += candle.ClosePrice;
You quickly get used to good things. It was a complete mess. All these options did not suit me. We needed a one-liner solution. Then I started to google and found the first link:
http://stackoverflow.com/questions/3221812/sum-of-elements-in-a-stdvectorThe shortest of the proposed solutions:
int sum = std::accumulate(vector.begin(), vector.end(), 0);
But what to do if you need to add the values ​​of only one of the fields. You can of course make a clever iterator that, when dereferencing, returns one of the fields ... But all this smacks of hard coding for such a simple task.
What to do?
The next 20-30 minutes of googling showed that there are
Boost Ranges and a couple of other libraries, but they all didn’t look like LINQ. At that very moment I felt the strength to write my own implementation and cover it with tests.
The main tasks for me were:
- Make the library as close to LINQ as possible.
- Make the whole functionality "deferred" (lazy)
This is how the project
boolinq appeared (the name combines the words
bool and
linq ). Put it on Google Code:
http://code.google.com/p/boolinq/ . And that's what I got:
int sum = boolinq::from(cnadles).sum([](Candle c){return c.ClosePrice;});
Of course, it looks a bit more complicated than LINQ. But, this is only due to the syntax of lambda expressions in the C ++ language. The structure of the code itself remains the same. The following functions are currently implemented:
Sequence Transformations:
- take (int)
- skip (int)
- concat (range)
- where (lambda)
- select (lambda)
- reverse ()
- orderBy ()
- orderBy (lambda)
- groupBy (lambda)
- distinct ()
- distinct (lambda)
- for_each (lambda)
Sequence aggregators:
- all ()
- all (lambda)
- any ()
- any (lambda)
- sum ()
- sum (lambda)
- avg ()
- avg (lambda)
- min ()
- min (lambda)
- max ()
- max (lambda)
- count ()
- count (lambda)
- contains (value)
- elementAt (int)
Export sequence:
- toSet ()
- toList ()
- toDeque ()
- toVector ()
- toContainer <T> ()
And even a few unusual:
- bytes ()
- bytes <ByteOrder> ()
- unbytes <t> ()
- unbytes <T, ByteOrder> ()
- bits ()
- bits <BitOrder> ()
- bits <BitOrder, ByteOrder> ()
- unbits ()
- unbits <BitOrder> ()
- unbits <T> ()
- unbits <T, BitOrder> ()
- unbits <T, BitOrder, ByteOrder> ()
Usage example
Here is an example expression:
int src[] = {1,2,3,4,5,6,7,8}; auto dst = from(src).where( [](int a){return a%2 == 1;})
Several operations are step-by-step applied to the original collection:
1. Leave only elements with an odd value.
2. Multiply the value of each element by 2.
3. Leave only elements with values ​​in the range (2.12).
4. Place the result in
std::vector
.
Or a more complex expression:
struct Man { std::string name; int age; }; Man src[] = { {"Kevin",14}, {"Anton",18}, {"Agata",17}, {"Terra",20}, {"Layer",15}, }; auto dst = from(src).where( [](const Man & man){return man.age < 18;}) .orderBy([](const Man & man){return man.age;}) .select( [](const Man & man){return man.name;}) .toVector();
The type of the
dst
variable will be
std::vector<std::string>
. The resulting vector will contain the following values: “Kevin”, “Layer”, “Agata”. Actions applied to the source array:
1. Leave in the array only people under 18 years old.
2. Arrange the elements in the array by increasing age.
3. Select only names from the array.
4. Place the result in
std::vector
.
Conclusion
The result was a deferred library for querying arrays, vectors, and other data containers. The speed of the functions is not inferior to the speed of a similar program, written using cycles. Syntax is as close as possible to LINQ. I enjoyed my time designing and developing the library functionality. The code is well covered with tests (I do not know how many percent, if someone tells me, I will be glad). There are functions that have no analogues in LINQ.
The library is distributed as a single header file
boolinq-all.h
. I would be glad if someone library will be useful. If there are suggestions for improvement, adding a function - please speak up. If you have time and desire to code - join. Anyone can leave comments on the code on Google Code. Also created a group for discussion on Google Groups:
https://groups.google.com/forum/?fromgroups#!forum/boolinq