LINQ to Objects in C ++

    It all started with the fact that I was at the institute and after graduation I wrote code in C ++ and did not know the troubles. But then one day I had to write code for .NET in C #. At first, he spat a little, but then nothing - he got involved. I saw beneficial differences from C ++: security, rigor, etc. I also could not ignore LINQ when working with collections ...



    Introduction to the issue


    But I appreciated all the charm of LINQ when it was time to return to C ++. It was a bit unusual to write in C ++ after a six-month break. Nothing boded ill, when suddenly I had to calculate the sum of the elements in the vector, more specifically the sum of the fields of the elements of the 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 you rewrite 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 ++ 11 language standard promises us simplification,
    but Visual Studio 2010 alas does not support this feature , but how ... (thanks Damaskus ):

    int sum = 0;
    for (Candle candle : vector) 
        sum += candle.ClosePrice;

    You quickly get used to the good. It was a complete mess. All of these options did not suit me. We needed a one-line solution. Then I began to google and found on the very first link: http://stackoverflow.com/questions/3221812/sum-of-elements-in-a-stdvector

    The 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 cunning iterator that, when dereferenced, returns one of the fields ... But it all 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 looked different from what LINQ looks like. At that very moment, I felt the strength in myself - to write my implementation and cover it with tests.

    The main tasks for me were:
    • Make the library as similar as possible to LINQ
    • Make all the functionality “deferred” (lazy)


    This is how the boolinq project appeared (the name combines the words bool and linq ). Posted it on Google Code: http://code.google.com/p/boolinq/ . And here is 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 C ++. The very structure of the code remains the same. Currently, the following functions are implemented:

    Transformations of sequences:
    • 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()

    And even a few unusual ones:
    • bytes ()
    • bytes()
    • unbytes()
    • unbytes()
    • bits ()
    • bits()
    • bits()
    • unbits ()
    • unbits()
    • unbits()
    • unbits()
    • unbits()


    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;})    // 1,3,5,7
                        .select([](int a){return a*2;})         // 2,6,10,14
                        .where( [](int a){return a>2 && a<12;}) // 6,10
                        .toVector();

    Several operations are applied step by step to the original collection:
    1. Leave only elements with an odd value.
    2. Multiply the value of each element by 2.
    3. Leave only the elements with values ​​in the range (2,12).
    4. The result is placed 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 variable dstwill be . The resulting vector will contain the following values: "Kevin", "Layer", "Agata". Actions applied to the original array: 1. Leave only people under the age of 18 in the array. 2. Arrange the elements in the array by increasing age. 3. Select only names from the array. 4. The result is placed in .std::vector




    std::vector

    Conclusion


    The result is a library of deferred queries to arrays, vectors, and other data containers. The speed of functions is not inferior to the speed of a similar program written using cycles. The syntax is as close as possible to LINQ. I enjoyed spending time designing and developing the functionality of the library. The code is well covered by tests (I don’t 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 fileboolinq-all.h. I would be glad if someone finds the library useful. If there are suggestions for improvement, adding functions - please speak out. If you have time and desire to hang around - join. Everyone can leave comments on the code on Google Code. A discussion group has also been created on Google Groups: https://groups.google.com/forum/?fromgroups#!forum/boolinq

    Also popular now: