30 maja 2015

[C++11] Przekazywanie zmiennych do wątku

Najpierw kilka przydatnych metod i obiektów do eksperymentowania z std::thread i std::this_thread.
  • hardware_concurrency - informuje ile wątków ze wsparciem sprzętowym jest dostępnych w systemie. Jeżeli planujemy zrównoleglić naszą pracę, trzeba pamiętać, że jeden wątek jest już zajęty przez nasz program.
  • get_id - metoda wołana z std::this_thread albo na obiekcie wątku. Pozwala zorientować się z jakim wątkiem mamy do czynienia. Standard nie definiuje co to będzie, ale gwarantuje, że taki obiekt (std::thread::id), może być porównywany i wyświetlany na standardowym wyjściu.
  • sleep_for - pozwala uśpić wątek na pewien czas.

Konstruktor std::thread ma podobną konstrukcję do std::bind. Trzeba pamiętać, że domyślnie wszystkie przekazane zmienne są kopiowane. Gdy interesuje nas aby wątek wyliczył i zapisał jakąś wartość do zmiennej przekazanej przez referencję, należy skorzystać z std::ref (tak jak w przypadku std::bind).
#include <iostream>
#include <thread>

using namespace std;

struct Presenter {
    void operator()(int& value) {
        cout << "Slave id: " << std::this_thread::get_id() << endl;
        value += 100;

        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
};

int main() {
    cout << "Possible threads: " << std::thread::hardware_concurrency() << endl;
    cout << "Master id: " << std::this_thread::get_id() << endl;

    int value = 1;

    std::thread t{ Presenter(), std::ref(value) };
    cout << "Child id from master: " << t.get_id() << endl;

    t.join();
    return 0;
}
Wynik:
Possible threads: 4
Master id: 140557572298560
Child id from master: 140557555422976
Slave id: 140557555422976
Kłopotliwe mogą okazać się wszelkie niejawne rzutowania. W przykładzie poniżej, funkcja fun() przyjmuje jako argument std::string. Wykonanie programu będzie przebiegało w ten sposób. W pierwszej kolejności do wątku przekazany zostanie wskaźnik (char const*), a następnie wykonana zostanie konwersja do std::string (już w kontekście nowego wątku). Istnieje bardzo duża szansa, że gdy konwersja się zadzieje, zmienna z głównego wątku nie będzie już istnieć, a działanie programu będzie trudne do przewidzenia. Rozwiązaniem jest wykonanie jawnego rzutowania przed przekazaniem zmiennych do std::thread.
#include <iostream>
#include <string>
#include <thread>

using namespace std;

void fun(std::string s) {
    cout << "Received: " << s << endl;
}

int main() {
    char buf[10];
    for (int i = 0 ; i < 3; i++) {
        std::sprintf(buf, "%d", i);
        cout << "Setting: " << buf << endl;

        std::thread t{ fun, buf };
//      std::thread t{ fun, std::string(buf) };    // OK!

        t.detach();
    }
    std::sprintf(buf, "%d", 77);
    std::this_thread::sleep_for(std::chrono::seconds(1));
}
Wynik. Jeden z wątków dokonuje konwersji z char* na std::string, gdy wątek główny zmienił już zawartość bufora na 77 (linijka 22).
Setting: 0
Setting: 1
Received: 1
Received: 2
Setting: 2
Received: 77

19 maja 2015

[C++] Aggregate, trivial classes, standard-layout

Nowy standard wprowadza kilka definicji, które trzeba przyswoić.

Aggregate


Agregaty (tablice lub klasy), które mogą być inicjalizowane nawiasami klamrowymi (nie ma to nic wspólnego z std::initializer_list).
Jeżeli elementów, które podamy w klamrach będzie mniej, niż tych zadeklarowanych, to dla brakujących elementów zostanie (o ile to możliwe) zawołany konstruktor domyślny.
Agragaty, nie mogą mieć:
  • konstruktora stworzonego przez użytkownika (ale user-declared jest w porządku)
  • niestatycznych składowych zainicjowanych w miejscu deklaracji
  • prywatnych lub chronionych niestatycznych pól składowych
  • klas bazowych
  • metod wirtualnych
#include <iostream>
#include <string>
#include <vector>

struct AggregateClass {
//  AggregateClass() {}           // ERROR - stworzone przez użytkownika
    AggregateClass() = default;   // OK - "user-declared"

    int a;
    std::string b;
    std::vector<double> c;
};

int main() {
    AggregateClass ag = {3, "text", {1.41, 3.14}};
}

Trivial classes


Klasy trywialne wspierają statyczną inicjalizację typu. Klasa trywialna to klasa, która ma trywialny konstruktor domyślny i jest trywialnie kopiowalna.
Klasa trywialnie kopiowalna (trivially copyable), to taka, której instancje mogą być kopiowane bitowo np. przy pomocy std::memcpy().

W opisie metod traitsów, są szczegółowe wymagania:
#include <iostream>
#include <type_traits>

struct Trivial {
    Trivial() = default;

    int value;
};

int main() {
    std::cout << std::boolalpha;

    std::cout << std::is_trivial<Trivial>::value << "\n";
    std::cout << std::is_trivially_copyable<Trivial>::value << "\n";
    std::cout << std::is_trivially_default_constructible<Trivial>::value << "\n";
}
Wynik:
true
true
true

Standard-layout


Typy standard-layout mają takie same ułożenie pól w pamięci jak struktury w języku C. Warunki, które muszą spełniać

POD - przestarzałe od C++20


Typy POD, są kompatybilne bitowo ze strukturami języka C. W nowej wersji standardu wprowadzono nową (uszczegółowioną) definicję typów POD (plain old data). Typem POD jest klasa, która jest jednocześnie trivial classes i standard-layout i jest to właściwość spełniona (rekursywnie) dla wszystkich jej pól.
W C++20 std::is_pos zostało usunięte.

Przykład dla POD i standard-layout

#include <iostream>
#include <type_traits>

struct A {
    int field;
};

struct B {
    int field1;
private:
    int field2;
};

int main()
{
    std::cout << std::boolalpha;

    std::cout << "A trivial: " << std::is_trivial<A>::value << "\n";
    std::cout << "A standard_layout: " << std::is_standard_layout<A>::value << "\n";
    std::cout << "A pod: " <<std::is_pod<A>::value << "\n";

    std::cout << "\n";
    std::cout << "B trivial: " << std::is_trivial<B>::value << "\n";
    std::cout << "B standard_layout: " << std::is_standard_layout<B>::value << "\n";
    std::cout << "B pod: " << std::is_pod<B>::value << "\n";
}
Wynik:
A trivial: true
A standard_layout: true
A pod: true

B trivial: true
B standard_layout: false
B pod: false

14 maja 2015

[C++] std::thread is low level primitive

Na obiektach std::thread zawsze należy wołać join() lub detach(), a brak decyzji skutkuje poważnym błędem. Operacje tworzenia obiektu i "decyzja" o jego losie są rozłączne, co niechybnie zachęca do popełniania pomyłek. Większość książkowych przykładów zachęca do pisania własnych wraperów typu RAII, ale dlaczego sam standard nie oferuje czegoś podobnego? Odpowiedź przyniósł bardzo fajny wpis na blogu Andrzeja Krzemieńskigo.
std::thread należy rozpatrywać jako nisko poziomowy (low level) prymityw. std::thread jest tylko budulcem do tworzenia wysoko pozimowych rozwiązań, tak jak delete, jest budulcem, dzięki któremu może istnieć std::unique_ptr. W momencie wprowadzania tego rozwiązania komitetowi standaryzacyjnemu po prostu zabrakło czasu, na coś więcej. Tak jak należy wystrzegać się delete, w produkcyjnym kodzie, tak samo powinno być traktowane std::thread. Być może boost::strict_scoped_thread będzie dobrym rozwiązaniem.
#include <iostream>
#include <thread>
#include <boost/thread/scoped_thread.hpp>

void f()
{
    std::cout << "Hello" << std::endl;
}

int main()
{
    std::thread t1(f);
    t1.join();

    boost::strict_scoped_thread<> t2((boost::thread(f)));  // podwójne nawiasy

    return 0;
}
I jeszcze konfiguracja CMakeLists.txt
project(cpp_thread)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

find_package(Boost COMPONENTS system thread REQUIRED)
target_link_libraries(${PROJECT_NAME}
    pthread
    ${Boost_SYSTEM_LIBRARY}
    ${Boost_THREAD_LIBRARY}
)

4 maja 2015

Vim - ściąga

Robię wszystko, aby nie musieć korzystać z narzędzia jakim jest vim, ale często jest to jedyna alternatywa, do wystukiwania echo na konsoli. Kilka linków do opisów, ułatwiających pracę z tym narzędziem.

2 maja 2015

[C++Now!] Michael Caisse: Introduction to Modern C++ Techniques

Dwa ciekawe wykłady, na temat nowoczesnych technik stosowanych w C++ (choć z 2012 roku). To co mnie zainteresowało (a szczególnie ostatni punkt):
  • Traits
  • Policy Classes
  • CRTP (Curiously Recurring Template Pattern)
  • SFINAE (Substitution Failure is Not An Error)
  • Units - jak radzić sobie z jednostkami fizycznymi