LINQ for C++ with cpplinq

4. April 2014

 

When it comes to programming languages, I literally use dozens, but when it comes down to an all other things being equal decision my go to language of choice tends to be C#.  One of the big plus sides of C# is the wonderful LINQ ( Language Integrated Query ).  LINQ makes heavy use of lambda (closures) a feature lacking until recently in C++.  Now with lambda expressions part of the C++ language LINQ for C++ is now a possibility.  It exists as a single hpp file you add to your project.

 

If you aren’t already familiar with LINQ, here is a simple example in C#.  It’s a simple scoreboard that sorts the results by name, then by score, then totals and averages all of the scores.  As you can see, it’s a very compact and clean way to access data.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Entry
    {
        public Entry(string name, int score)
        {
            this.name = name; 
            this.score = score;
        }

        public string name { get; set; }
        public int score { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Entry> scoreEntries = new List<Entry>();
            scoreEntries.Add(new Entry("mike", 42));
            scoreEntries.Add(new Entry("bob", 99));
            scoreEntries.Add(new Entry("doug", 99));
            scoreEntries.Add(new Entry("steve", 12));

            var sortedByName = scoreEntries.OrderBy(item => item.name).ToList();
            Console.WriteLine("Sorted by score");
            sortedByName.ForEach(item => { Console.WriteLine(item.name + " " + item.score); });

            Console.WriteLine("\nSorted by name");
            var sortedByScore = scoreEntries.OrderBy(item => item.score).ToList();
            sortedByScore.ForEach(item => { Console.WriteLine(item.name + " " + item.score); });

            var totalOfScores = scoreEntries.Where(item => item.score > 0)
                .Sum(item => item.score);

            var averageScore = scoreEntries.Average(item => item.score);
            Console.WriteLine("\nTotal of scores == " + totalOfScores + " Average Score == " + averageScore);

            
        }
    }
}

 

Now let's take a look at the C++ version using cpplinq:

#include <string>
#include <list>
#include "cpplinq.hpp"
#include <iostream>


class Entry{
    public:
        Entry::Entry(std::string name, int score){
            this->name = name;
            this->score = score;
        }

        std::string name;
        int score;
};


int main(int argc, char **argv)
{
    std::list<Entry> scoreEntries;
    scoreEntries.push_back(Entry("mike", 42));
    scoreEntries.push_back(Entry("bob", 99));
    scoreEntries.push_back(Entry("doug", 99));
    scoreEntries.push_back(Entry("steve", 12));

    using namespace cpplinq;
    
    auto sortedByName = from(scoreEntries)
        >> orderby_ascending([](const Entry & entry){ return entry.name;  })
        >> to_vector();
        
    auto sortedByScore = from(scoreEntries)
        >> orderby_descending([](const Entry & entry){ return entry.score;  })
        >> to_vector();

    std::cout << "Sorted by name" << std::endl;
    from(sortedByName)
        >> for_each([](const Entry & entry){ std::cout << entry.name << " " << entry.score << std::endl; });

    std::cout << std::endl << "Sorted by score" << std::endl;
    from(sortedByScore)
        >> for_each([](const Entry & entry){ std::cout << entry.name << " " << entry.score << std::endl; });

    auto totalOfScores = from(scoreEntries)
        >> select([](const Entry & entry){ return entry.score; })
        >> sum();

    auto averageScore = from(scoreEntries)
        >> select([](const Entry & entry){ return entry.score; })
        >> avg();

    std::cout << std::endl << "Total of scores == " << totalOfScores << " average score == " << averageScore << std::endl;
    return 0;
}

 

A few obvious differences.  Cpplinq statements start with the from methods defining the type of data source to perform on.  In this case I used the default from() which takes a standard C++ STL type.  There are also from__array() and from_range() for working with arrays and iterators respectively.  Next the . (dot) operator has been replaced with the >> operator.  Of course the lambda syntax is different as well ( C++’s is much uglier ), otherwise at the end of the day, it is very similar to LINQ in both look and execution.

 

If you are new to C++ and are struggling to wrap your head around the uses for lambdas, give cpplinq a shot and you will quickly see their value!

Programming







blog comments powered by Disqus