Post on 26-Jun-2015
description
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
λ
Author
Blogger Open-Source Contributor
LEESA
Rx4DDS.NET
RefleXReflection for DDS-XTypes
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
» 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); });}
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);
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);
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);
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);
» 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) };
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
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
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;}
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;}
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
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();
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
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
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/
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()"
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());
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
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;})
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);
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
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);
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(); });}
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);
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);}
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]; });}
Thank You!