chapter 2 Concept

Language feature

  1. auto
    • Defer to define variable type. (Like var in c#/JS.)
    • it will discard const, volatile and reference.
  2. lambda
    • Anonymous function (object)
    • Syntax [=] () mutable throw() -> typeid
  3. constexpr
    • Keyword (modifier) similiar to 'const'
    • Initialized in compile time. ('const' in runtime)
    • Can modify both type and function. ('const' only modifies type)
  4. range base for loop

    • Since C++11, make traditional for-loop more readable, such as all elements in a container.
    // captures_lambda_expression.cpp
    // compile with: /EHsc
    #include "stdafx.h"
    #include <iostream>
    #include <iterator>
    #include <algorithm>
    #include <vector>
    
    // fibonacci in compile time by constexpr
    constexpr int fabonacci(int n) {
     return (n < 2) ? 0 : 
       (fabonacci(n - 1) + fabonacci(n - 2));
    }
    
    // fibonacci in compile time by meta-function
    template <int N>
    struct fab_meta {
     enum { val = fab_meta<N - 2>::val + fab_meta<N - 1>::val };
    };
    template <> struct fab_meta<0> { enum { val = 1 }; };
    template <> struct fab_meta<1> { enum { val = 1 }; };
    
    using namespace std;
    int main()
    {
     const int N = 10;
     vector<int> vec(2, 1);
     for (int i = 2; i < N; i++) {
       vec.push_back(vec[i - 2] + vec[i - 1]);
     }
     std::for_each(vec.begin(), vec.end(), [](int i) { cout << i << " "; });
     cout << endl;
    
     vector<int> series;
     series = { fabonacci(0), fabonacci(1),  fabonacci(3),  fabonacci(4),  fabonacci(5), };
     for(int fn : vec)
       cout << fn << ",";
     cout << endl;
    
     series = { fab_meta<0>::val, fab_meta<1>::val,  fab_meta<2>::val,  fab_meta<3>::val,  fab_meta<4>::val, fab_meta<5>::val };
     for (auto fn : vec)
       cout << fn << ",";
    }
    // Output: 
    // 1,1,2,3,5,8,13,21,34,55
    // 1,1,2,3,5,8,13,21,34,55
    // 1,1,2,3,5,8,13,21,34,55
    
  5. Syntax compare (C11/14 vs C# vs Java Script)

    std::vector<int> v = {0, 1, 2, 3, 4, 5};
    for (int it = v.begin(); it != v.end(); ++it) // Safer than C
    { std::cout << i << ' '; }
    
    auto show = [](const int& i) { std::cout << i << ' '; };   // Elegant: lib-level
    std::for_each(v.begin(), v.end(), show) {};
    
    for (const int& i : v) // Syntax sugar: lang-level
    { std::cout << i << ' '; }
    
    int[] v = new int[] {0, 1, 2, 3, 4, 5};
    foreach(int i in v) 
    { System.Console.Write("{0};",i); }
    
    var v = [0, 1, 2, 3, 4, 5];
    v.forEach( function(i) {
      console.log(i);
    }
    v.map(function(i) { // accumulates all of the results into a collection
      console.log(i);
      return i;
    });
    

Move semantics and memory management

  1. rvalue reference (&&)

    • For implement "move semantics" and "perfect forwarding" (reduce copy times)
  2. rule of 5

    • copy constructor
    • copy assignment operator
    • destructor
    • move constructor
    • move assignment operator
    class resource {
    int x = 0;
    };
    class foo
    {
    resource* p;
    public:
    foo() : p{ new resource{} } 
    ...
    // move constructor
    foo(foo&& other) : p{ other.p }
    { other.p = nullptr; }
    
    // move assignment operator
    foo& operator=(foo&& other) {
     if (&other != this) {
       delete p;
       p = other.p;
       other.p = nullptr;
     }
     return *this;
    }
    };
    
  3. std::move std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.

  4. std::forward

    template<class T>
    void wrapper(T&& arg) {
     foo(std::forward<T>(arg)); // Forward a single argument.
    }
    
    • If a call to wrapper() passes an rvalue std::string, then T is deduced to std::string (not std::string&, const std::string&, or std::string&&), and std::forward ensures that an rvalue reference is passed to foo.
    • If a call to wrapper() passes a const lvalue std::string, then T is deduced to const std::string&, and std::forward ensures that a const lvalue reference is passed to foo.
    • If a call to wrapper() passes a non-const lvalue std::string, then T is deduced to std::string&, and std::forward ensures that a non-const lvalue reference is passed to foo.
  5. Smart Pointer

    • Replace pointer by template class XXX_ptr<> to avoid memory leak
    • std::unique_ptr - delete resource after object destroy (leve scope)
    • std::shared_ptr - reference count (static data member), increase when operator=
      shared_ptr<int> a;  // a is empty
      void func()
      {
       shared_ptr<int> b( new int( 10 ) );
       a = b; 
       *a = 100;
       ...
      }
      
    • std:weak_ptr - could point to both, lazy lock

Parallel Pattern library (PPL)

  1. Overview:

    • The PPL provides a imperative programming model that resembles the Standard Template Library (STL) and promotes scalability and ease-of-use for developing concurrent applications.
    • Follow chapter 0, and compile with Precompiled HeaderNot Using Precompiled Headers.

      template <class Function>
      __int64 time_call(Function&& f) {
        __int64 begin = GetTickCount();
        f();
        return GetTickCount() - begin;
      }
      int fibonacci(int n) {
        retunr (n < 2)? 1: fibonacci(n-1) + fibonacci(n-2);
      }
      int wmain()
      {
        __int64 elapsed;
        array<int, 4> a = { 24, 26, 41, 42 }; // big Fibonacci numbers
        vector<tuple<int,int>> results1; // result of serial computation
        concurrent_vector<tuple<int,int>> results2; // result of parallel computation
      
        // Use the for_each algorithm to compute the results serially.
        elapsed = time_call([&] {
           for_each (begin(a), end(a), [&](int n) {
              results1.push_back(make_tuple(n, fibonacci(n)));
           });
        });   
        wcout << L"serial time: " << elapsed << L" ms" << endl;
      
        // Use the parallel_for_each algorithm to perform the same task.
        elapsed = time_call([&] {
           parallel_for_each (begin(a), end(a), [&](int n) {
              results2.push_back(make_tuple(n, fibonacci(n)));
           });
           sort(begin(results2), end(results2));
        });   
        wcout << L"parallel time: " << elapsed << L" ms" << endl << endl;
        for_each (begin(results2), end(results2), [](tuple<int,int>& pair) {
           wcout << L"fib(" << get<0>(pair) << L"): " << get<1>(pair) << endl;
        });
      }
      
    • Fork Way // Ugly and more overloading
      long fib_par(long n) {
          long result;
          if (n < 2) {
            result = n;
          } else {
              long f0, f1;
              fork2([&] { // lambda: call by all reference
                  f1 = fib_par(n-1);
              }, [&] { // lambda: call by all reference
                  f0 = fib_par(n-2);
              });
              result = f0 + f1;
          }
          return result;
      }
      
  2. std::thread
    • The class thread represents a single thread of execution.
  3. std::mutex
    • The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. (critical section)
  4. std::future
    • A mechanism to access the result of asynchronous operations.
  5. std::promise
    • The class template std::promise provides a facility to store a value or an exception that is later acquired asynchronously.
    • Each promise is associated with a shared state, which contains some state information and a result which may be not yet evaluated
// thread, mutex
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>

std::map<std::string, std::string> g_pages;
std::mutex g_pages_mutex;
 void save_page(const std::string &url)
{
    // simulate a long page fetch
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::string result = "fake content";

    std::lock_guard<std::mutex> guard(g_pages_mutex);
    g_pages[url] = result;
}
int main() 
{
    std::thread t1(save_page, "http://foo");
    std::thread t2(save_page, "http://bar");
    t1.join();
    t2.join();

    // safe to access g_pages without lock now, as the threads are joined
    for (const auto &pair : g_pages) {
        std::cout << pair.first << " => " << pair.second << '\n';
    }
}
Output:
http://bar => fake content
http://foo => fake content
// future, promise
#include <iostream>
#include <future>
#include <thread>

int main()
{
    // future from a packaged_task
    std::packaged_task<int()> task([](){ return 7; }); // wrap the function
    std::future<int> f1 = task.get_future();  // get a future
    std::thread(std::move(task)).detach(); // launch on a thread

    // future from an async()
    std::future<int> f2 = std::async(std::launch::async, [](){ return 8; });

    // future from a promise
    std::promise<int> p;
    std::future<int> f3 = p.get_future();
    std::thread( [&p]{ p.set_value_at_thread_exit(9); }).detach();

    std::cout << "Waiting..." << std::flush;
    f1.wait();
    f2.wait();
    f3.wait();
    std::cout << "Done!\nResults are: "
              << f1.get() << ' ' << f2.get() << ' ' << f3.get() << '\n';
}
Output:
Waiting...Done!
Results are: 7 8 9

results matching ""

    No results matching ""