Skip to content

Instantly share code, notes, and snippets.

@yorickdewid
Created January 3, 2015 09:54
Show Gist options
  • Select an option

  • Save yorickdewid/6c862316bbe75e5654a6 to your computer and use it in GitHub Desktop.

Select an option

Save yorickdewid/6c862316bbe75e5654a6 to your computer and use it in GitHub Desktop.
Example code of the C++11 features
/// This is a demonstration of a few nice C++11 features: auto, the ':'
/// iterator, initializer lists, std::function, and lambda expressions. In
/// many cases, it's possible to achieve the same effect as I present using
/// even less code, but by being explicit I hope I have made the code and
/// comments easy to follow
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std; // I'm being lazy here... should explicitly use cout,
// endl, function, map, pair, string, and vector
/// wrapped_map_t extends map<string, string>. We start with a map of
/// key/value pairs, where both the key and value are strings, and we add a
/// new method to it. The new method, apply_to_all, lets us apply a lambda
/// to (a copy of) every key/value pair that is in the map.
///
/// my claim is that lambdas fundamentally change the art of data structure
/// design. To support my claim, I will show how this one simple function
/// obviates many public functions that one might want int a data structure
/// (count, print, extract_keys, find_matching_keys, etc).
struct wrapped_map_t : public map<string, string>
{
/// the apply_to_all function simply iterates through the key/value pairs
/// of the map, calling lambda(key, value) for each pair. Strictly
/// speaking, std::map is a red/black tree, and this will do a pre-order
/// traversal
void apply_to_all(function<void(const string, const string)>& lambda)
{
// note the use of C++11 'auto' keyword to avoid having to specify
// the type of the iterator
for (auto i = begin(), e = end(); i != e; ++i)
lambda(i->first, i->second);
}
};
int main()
{
// declare an instance of the extended map
wrapped_map_t kvpairs;
// populate the map with some keys and values. We're going to use some
// great C++ magic here: initializer lists. We don't even have to know
// the type of the list, but the compiler will deduce that it's something
// like pair<string,string>[]
kvpairs.insert({{"donald", "duck"}, {"mickey", "mouse"},
{"minnie", "mouse"}, {"bat", "man"},
{"super", "man"}, {"milo", "otis"},
{"mighty", "mouse"}, {"rocky", "squirrel"},
{"bullwinkle", "moose"}});
// 'func' is a reference to a function that takes two strings as its
// parameters, and returns nothing. This is a lot nicer than the old C
// syntax in use before C++11 (i.e., void (*func)(string, string)).
function<void(string, string)> func;
// let's assign a new lambda to func. the lambda will simply print its
// parameters
func = [](string k, string v) { cout << "{" << k << ", " << v << "} "; };
// now let's apply func to every element in the set. No need for a
// "print_in_sorted_key_order" function in our class. It would be
// trivial to add "print keys" and "print values" features without
// changing the data structure... we'd just need to pass different
// lambdas
cout << "KVPairs = ";
kvpairs.apply_to_all(func);
cout << endl;
// The last example was pretty boring. A function pointer could have
// done that easily. Now let's pass in a function that has a captured
// reference to a variable that is local to this function. In that way,
// each time the lambda is called, it can access the function 'my_count'
// that we declare *right here*. The net result is that we can count the
// elements in the map, without requiring map to provide a "count()"
// function. Note that the parameters to the lambda aren't used, and
// that's OK.
int my_count = 0;
func = [&my_count](string k, string v) { my_count++; };
kvpairs.apply_to_all(func);
cout << "Elements in map = " << my_count << endl;
// OK, so we could have achieved that effect with an apply_to_all that
// took three parameters: string, string, int&. But would we really want
// to do that for every possible pass-by-reference type? For example,
// here we pass a vector of strings so that we can copy out all keys from
// the map
vector<string> keys;
func = [&keys](string k, string v) {keys.push_back(k);};
kvpairs.apply_to_all(func);
// print the keys, to show that it worked
cout << "Keys in map = {";
for (auto i : keys)
cout << i << ",";
cout << "}" << endl;
// note, too, that we can use the lambda to capture multiple local
// variables, and that some can be pass by value while others are pass by
// reference. Here 'pre' is pass by value, 'keys' is pass by reference,
// and our function copies into 'keys' all keys in the map whose values
// begin with 'mo'
string pre = "mo";
keys.clear();
func = [&keys, pre](string k, string v) { if (v.find(pre) == 0) keys.push_back(k);};
kvpairs.apply_to_all(func);
cout << "Keys whose values start with 'mo' = {";
for (auto i : keys)
cout << i << ",";
cout << "}" << endl;
// this example is much like the prior one. Here we extract all
// key/value pairs where the key begins with 'mi'
pre = "mi";
vector<pair<string, string>> pairs;
func = [&pairs, pre] (string k, string v)
{ if (k.find(pre) == 0) pairs.push_back(make_pair(k, v)); };
kvpairs.apply_to_all(func);
cout << "Elements whose key begins with 'mi' = {";
for (auto i : pairs)
cout << "{"<<i.first<<", "<<i.second<<"} ";
cout << "}" << endl;
// the point of this example is to illustrate that when the lambda is
// called with the key and value, the function we are executing *cannot*
// modify the internals of the map. Put another way, the lambda does not
// become a member function of the class. So, for example, if we wanted
// to add a suffix to every key, then the only way we could do it would
// be by explicitly updating the k/v pair in the map.
string suf = " <3";
// note: we need to capture *kvpairs itself* by reference
func = [&kvpairs, suf] (string k, string v) { kvpairs[k] = v+suf; };
kvpairs.apply_to_all(func);
// use a lambda to print it out
func = [](string k, string v) { cout << "{" << k << ", " << v << "} "; };
cout << "Modified kvpairs = ";
kvpairs.apply_to_all(func);
cout << endl;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment