Channels ▼
RSS

Open Source

LINQ-like List Manipulation in C++


As happens in LINQ, with Cpplinq, you can use the std::list<game,std::allocator<game>> generated in the previous query (highest_scores_for_played_games) as an input for another LINQ-style query and apply operators to the list. The following lines show a simple example that uses the from range source to create a range from the std::list<game,std::allocator<game>> to serve as a kick-off for a new query. Then, the query uses the select project operator to retrieve just the game's name and the first_or_default element operator to get the first game's name or the default value. The result is the same as in the first example (which used first_or_default) because the sum of all the operators included in that first and in this second query is equivalent to the query in the first example.

auto first_highest_score_for_played_games = from(highest_scores_for_played_games)
       >> select([](game const & g){return g.name; })
       >> first_or_default();

Working with Range Sources Supported in Cpplinq

One of the main differences between the LINQ-style queries generated with Cpplinq and the original LINQ queries in .NET is the way you have to specify range sources. The following list shows the different range sources that Cpplinq supports:

  • from: Creates a range from a STL-like container such as std::list and std::vector.
  • from_array: Creates a range from an Array.
  • from_iterators: Creates a range from the pair of iterators specified as parameters.
  • range: Creates an int range from the start integer specified as the first parameter (start) and the desired number of integers specified as the second parameter (count).

The latest example can be rewritten using from_iterators instead of from. The following lines show the use of from_iterators to create a range from the pair of iterators highest_scores_for_played_games.cbegin and highest_scores_for_played_games.cend.

auto first_highest_score_for_played_games = from_iterators(highest_scores_for_played_games.cbegin(), highest_scores_for_played_games.cend())
       >> select([](game const & g){return g.name; })
       >> first_or_default();

The following C# code is the popular example of the use of the IEnumerable.Range method. The code uses LINQ to generate a sequence of integers from 1 to 10 and then select their squares:

IEnumerable<int> squares = Enumerable.Range(1, 10).Select(x => x * x);

With Cpplinq, you can use range to produce the same results in a very similar LINQ-style query. The following lines show the code for a C++ console application that uses a Cpplinq LINQ-style query with range, select, and to_list. Then, the code displays the results in the console output.

#include "stdafx.h"
#include "cpplinq.hpp"
#include <iostream>
#include <sstream>
#include <string.h>

int _tmain(int argc, _TCHAR* argv[])
{
	using namespace cpplinq;

	auto squares = range(1, 10)
		>> select([](int i) { return i * i; })
		>> to_list();

	for(auto num : squares)
	{
		std::wcout << num << std::endl;
	}

	return 0;
}

I you are a big fan of LINQ-style queries, you probably don't like the idea of writing iteration code to perform an action per value in range. Cpplinq allows you to use for_each and provide a lambda that specifies a function to be applied once per value in range. The following lines show a simplified version of the previous code that includes the instructions to display the results within the LINQ-style query by using for_each. However, as happens in the original .NET LINQ implementation, you have to be careful with the things you code within the lambda specified in for_each in order to avoid having performance problems.

using namespace cpplinq;
range(1, 10)
	>> select([](int i) { return i * i; })
	>> for_each([](int i) { std::wcout << i << std::endl; });

Working with Range Aggregators

A LINQ-style query implementation wouldn't be complete without the support of range aggregators. The only LINQ range aggregator that Cpplinq doesn't support is LongCount.

The following lines show a LINQ-style query that uses the where restriction operator and the count range aggregator to calculate the total number of played games in our sample:

using namespace cpplinq;
auto played_games_count = from_array(games)
       >> where([](game const & g) { return g.played_count > 0; })
       >> count();

As you might guess, the max range aggregator makes it easy to use a LINQ-style query to obtain the highest score for all the played games. Note that the query uses the select project operator with a lambda expression that returns the desired field, high_score, and then there is a chained call to the max range aggregator to calculate the maximum value for this field:

auto highest_score = from_array(games)
       >> where([](game const & g) { return g.played_count > 0; })
       >> select([](game const & g){return g.high_score; })
       >> max();

Conclusion

The code for Cpplinq is easy to understand and extend, so you can also take advantage of it to extend Cpplinq to fit your specific needs. I'm sure you will enjoy working with LINQ-style queries with Cpplinq and C++11 lambdas. If you have ever worked in .NET and written LINQ code, you'll find you can now enjoy many of its benefits in C++.


Gaston Hillar is a frequent contributor to Dr. Dobb's.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video