Fun with Lambdas: C++14 Style (part 2)

30
Fun with Lambdas C++14 Style (part 2) Sumant Tambe, Ph.D. Microsoft Visual C++ MVP Senior Software Research Engineer Real-Time Innovations, Inc. @ sutambe SFBay Association of C/C++ Users April 9, 2014 λ

description

Look at some interesting examples of C++14 lambdas and how they interact with other language features and libraries.

Transcript of Fun with Lambdas: C++14 Style (part 2)

Page 1: Fun with Lambdas: C++14 Style (part 2)

Fun with Lambdas C++14 Style (part 2)

Sumant Tambe, Ph.D.Microsoft Visual C++ MVPSenior Software Research EngineerReal-Time Innovations, Inc. @sutambe

SFBay Association of C/C++ UsersApril 9, 2014

λ

Page 3: Fun with Lambdas: C++14 Style (part 2)

Functional Programming is Really Out There

» Functional Programming eXchange» Strange Loop» ReactConf» LambdaConf» LambdaJam» CraftConf» MSFT MVP Summit» Qcon NYC/SF/London» Closure West» Spring into Scala » Progressive F#» FP Days» SkillsMatter

Page 4: Fun with Lambdas: C++14 Style (part 2)

» Lambda Expressions˃ expr.prim.lambda

» Anonymous functions

Lambda Cometh to C++

void abssort(float* x, unsigned N) { std::sort(x, x + N, [](float a, float b) { return std::abs(a) < std::abs(b); });}

Page 5: Fun with Lambdas: C++14 Style (part 2)

Good Old C++class Comp { float a;public: Comp(float x) { a = x; }

bool compare(float b) const { return std::abs(a) < std::abs(b); }};

float array[5] = { 0, 1, 2, 3, 4 };float a = 3;Comp f(a);for(float item : array) std::cout << std::boolalpha << f.compare(item);

Page 6: Fun with Lambdas: C++14 Style (part 2)

Still, Good Old C++class Comp { float a;public: Comp(float x) { a = x; }

bool operator () (float b) const { return std::abs(a) < std::abs(b); }};

float array[5] = { 0, 1, 2, 3, 4 };float a = 3;Comp f(a);for(float item : array) std::cout << std::boolalpha << f(item);

Page 7: Fun with Lambdas: C++14 Style (part 2)

Not Valid C++class ##### { float a;public: Foo(float x) { a = x; }

bool operator () (float b) const { return std::abs(a) < std::abs(b); }};

float array[5] = { 0, 1, 2, 3, 4 };float a = 3;auto f = #####(a);for(float item : array) std::cout << std::boolalpha << f(item);

Page 8: Fun with Lambdas: C++14 Style (part 2)

In C++, Lambdas are just function objectsclass ##### { float a;public: Foo(float x) { a = x; }

bool operator () (float b) const { return std::abs(a) < std::abs(b); }};

float array[5] = { 0, 1, 2, 3, 4 };float a = 3;auto f = #####(a);auto f = [a](float b) { return std::abs(a) < std::abs(b) };for(float item : array) std::cout << std::boolalpha << f(item);

Page 9: Fun with Lambdas: C++14 Style (part 2)

» Anonymous functions» Written exactly in the place where it's needed» Can access the variables available in the

enclosing scope (closure)» May maintain state (mutable or const)» Can be passed to a function» Can be returned from a function» Deduce return type automatically» Accept generic parameter types (only in C++14)

Lambdas in C++11/14

[a](auto b) { return std::abs(a) < std::abs(b) };

Page 10: Fun with Lambdas: C++14 Style (part 2)

Lambda-based Techniques» Associative containers and lambdas» Recursive Lambdas» Composable list manipulation» Scope Exit Idiom» Overloaded Lambdas» Type Switch (simple pattern matching)» Converting shared_ptr between boost and std» In-place parameter pack expansion» Memoization

Page 11: Fun with Lambdas: C++14 Style (part 2)

Associative containers and lambdas» Associative containers: set, map, multiset, multimap˃ Use comparators˃ Example, std::set<int, std::less<int>>

» Can comparators be a lambda?» std::set<int, ???>

» Use std::function as the comparator type std::set<int, std::function<bool(int, int)>> numbers([](int i, int j) { return i < j; });

» Small-closure optimization may kick in˃ Check you compiler manual

Page 12: Fun with Lambdas: C++14 Style (part 2)

Recursive Lambdaauto make_fibo() { std::function<int(int)> recurse; recurse = [&](int n){ return (n<=2)? 1 : recurse(n-1) + recurse(n-2); }; return recurse;}

int main() { auto fibo = make_fibo(); std::cout << fibo(10) << std::endl; // 55 return 0;}

Page 13: Fun with Lambdas: C++14 Style (part 2)

Recursive Lambdaauto make_fibo() { return [](int n) { std::function<int(int)> recurse; recurse = [&](int n){ return (n<=2)? 1 : recurse(n-1) + recurse(n-2); }; return recurse(n); };}

int main() { auto fibo = make_fibo(); std::cout << fibo(10) << std::endl; // 55 return 0;}

Page 14: Fun with Lambdas: C++14 Style (part 2)

Composable List ManipulationIList<Box> boxes = /* ... */;int sumOfWeights = boxes.Where(b => b.Color == Color.RED) .Select(b => b.Weight) .Count();

List<Box> boxes = /* ... */;int sumOfWeights = boxes.stream() .filter(b -> b.getColor() == Color.RED) .map(b -> b.getWeight()) .sum();

C#

Java8

Page 15: Fun with Lambdas: C++14 Style (part 2)

Composable List Manipulation in C++» Example cpplinq (http://cpplinq.codeplex.com)

Box boxes[] = { … };int sum_of_weights = cpplinq::from_array(boxes) >> where([](const Box & box) { return box.color == Color.RED; }) >> select([](const Box & box) { return box.get_weight(); }) >> count();

Page 16: Fun with Lambdas: C++14 Style (part 2)

Cpplinq Query Operators» Restriction Operators

˃ Where

» Projection Operators˃ ref, select, select_many

» Partitioning Operators˃ take, take_while, skip, skip_while

» Join Operators˃ Join

» Concatenation Operators˃ Concat

» Ordering Operators˃ orderby, orderby_ascending or

orderby_descending

» Set Operators˃ distinct, union_with, intersect_with,

except

» Set Operators˃ distinct, union_with, intersect_with,

except» Conversion Operators

˃ to_vector, to_list, to_map, to_lookup

» Element Operators˃ first, first_or_default, last_or_default

» Generation Operators˃ range, repeat, empty, singleton,

generate,

» Quantifiers˃ any, all, contains,

» Aggregate Operators˃ Count, sum, min, max, avg,

aggregate» Other

˃ pair_wise, zip_with

Page 17: Fun with Lambdas: C++14 Style (part 2)

Similar projects» Cpplinq http://cpplinq.codeplex.com/» Narl (Not another range library) https://github.com/essennell/narl» boost.range http://www.boost.org/doc/libs/1_54_0/libs/range/» Linq http://pfultz2.github.io/Linq/» boolinq http://code.google.com/p/boolinq/» lx++ (part of Native-RX) https://rx.codeplex.com/» Linq++ https://github.com/hjiang/linqxx/» oven http://p-stade.sourceforge.net/oven/» cpplinq (same name but unrelated)

http://code.google.com/p/cpplinq/» linq https://

bitbucket.org/ronag/cppextras/src/master/linq/linq.hpp

Page 18: Fun with Lambdas: C++14 Style (part 2)

LEESA C++ Library» Language for Embedded

Query and Traversal» Tree Traversal˃ For XML data-binding code

generators˃ Visitors

» Think XPath queries embedded in C++˃ Typed (not string encoded)

» Generic Programming˃ Expression Templates˃ Meta-programming˃ C++ Concepts (no longer in C++11)

http://www.dre.vanderbilt.edu/LEESA/

Page 19: Fun with Lambdas: C++14 Style (part 2)

LEESA Examples<catalog>  <book>    <title>…</title>    <price>…</price>    <author>      <name>…</name>      <country>…</country>    </author>  </book>  <book>...</book>...</catalog>

LEESA:std::vector<name> author_names = eval(root, catalog() >> book() >> author() >> name());

XPath: "/book/author/name/text()"

Page 20: Fun with Lambdas: C++14 Style (part 2)

LEESA Examples» Find names of authors from USA» XPath: “//author[country/text() = ‘USA’]/name/text()”» LEESA:Catalog croot = load_catalog(“catalog.xml”);std::vector<Name> author_names = eval(croot, Catalog() >> DescendantsOf(Catalog(), Author()) >> Select(Author(), [](const Author &a) { return a.get_country() == “USA”; }) >> Name());

Page 21: Fun with Lambdas: C++14 Style (part 2)

Scope Exit Idiom

int main() { std::mutex m; m.lock(); SCOPE_EXIT(m.unlock()); /* Potential exceptions here */ return 0;}

Example only.Prefer std::lock_guard

Scope exit idiom ensures that resources are released

Page 22: Fun with Lambdas: C++14 Style (part 2)

Scope Exit Idiomtemplate <typename F>struct ScopeExit { ScopeExit(F f) : f(f) {} ~ScopeExit() { f(); } F f;};

template <typename F>ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f);};

#define DO_STRING_JOIN(arg1, arg2) arg1 ## arg2

#define SCOPE_EXIT(code) \ auto STRING_JOIN(scope_exit_, __LINE__) = MakeScopeExit([&](){code;})

Page 23: Fun with Lambdas: C++14 Style (part 2)

Overloaded Lambdas» Can you really do that!?

˃ As it turns out, YES!

template <class... F>struct overload : F... { overload(F... f) : F(f)... {} };

template <class... F>auto make_overload(F... f) { return overload<F...>(f...); }

auto f = make_overload([](int i) { /* print */ }, [](double d) { /* print */ }); f(10); f(9.99);

Page 24: Fun with Lambdas: C++14 Style (part 2)

Type Switch (Pattern matching)struct Base {};struct Derived : Base {};

template <class Poly>void test(Poly& p) { match(p)( [](int i) { cout << “int”; }, [](std::string &) { cout << “string”; }, [](Base &) { cout << "Base"; }, [](Derived &) { cout << "Derived"; }, otherwise([](auto x) { cout << "Otherwise”; }) ); }test(10); // inttest(std::string(“C++ Truths”)); // stringtest(Derived()); // Derivedtest(9.99); // Otherwise

Page 25: Fun with Lambdas: C++14 Style (part 2)

Converting shared_ptrboost and std» Boost and std::shared_ptr manage memory automatically» They both use their own reference counters» What happens when you convert from boost to std shared_ptr or vice

versa

template <typename T>boost::shared_ptr<T> make_shared_ptr(std::shared_ptr<T> ptr){ return boost::shared_ptr<T>(ptr.get());}

» Hint: They both support deleters˃ malloc = allocator˃ free = deleter

std::shared_ptr<char> buf((char *)malloc(10), free);

Page 26: Fun with Lambdas: C++14 Style (part 2)

Converting shared_ptrboost and std template <typename T>boost::shared_ptr<T> make_shared_ptr(std::shared_ptr<T> ptr) { return boost::shared_ptr<T>(ptr.get(), [ptr](T*) mutable { ptr.reset(); });}

template <typename T>std::shared_ptr<T> make_shared_ptr(boost::shared_ptr<T> ptr){ return std::shared_ptr<T>(ptr.get(), [ptr](T*) mutable { ptr.reset(); });}

Page 27: Fun with Lambdas: C++14 Style (part 2)

In-place parameter pack expansionvoid no_op(...) { }

template <class... T>void foreach(T... args) { no_op([=](){ cout << args << "\n"; return true; }()...); }

foreach(10, 20.2, true);

Page 28: Fun with Lambdas: C++14 Style (part 2)

Memoization» Avoid repeated calculations by caching function results

template <typename ReturnType, typename... Args>auto memoize(ReturnType (*func)(Args...))

int fibo(int) { … }

int main() { auto mem_fibo = memoize(fibo); std::cout << “Compute” << mem_fibo(10); std::cout << “Lookup” << mem_fibo(10);}

Page 29: Fun with Lambdas: C++14 Style (part 2)

Memoization» Avoid repeated calculations by caching function resultstemplate <typename ReturnType, typename... Args>auto memoize(ReturnType (*func)(Args...)){ std::map<std::tuple<Args...>, ReturnType> cache;

return ([=](Args... args) mutable { std::tuple<Args...> t(args...); if (cache.find(t) == cache.end()) { std::cout << "not found\n"; cache[t] = func(args...); } return cache[t]; });}

Page 30: Fun with Lambdas: C++14 Style (part 2)

Thank You!