25 grudnia 2014

clang-modernize

Ciekawe narzędzie przedstawione przez Chandlera Carrutha na GoingNative 2013:
Jego głównym zadaniem jest przerobienie istniejącego kodu, wprowadzając nowe mechanizmy ze standardu C++11. Nie jest tego wiele, ale pokazuje, jakie moce drzemią w samym clang-u. Testy przeprowadzałem na wersji 3.5
clang-modernize -add-override \
                -loop-convert \
                -pass-by-value \
                -replace-auto_ptr \
                -use-auto \
                -use-nullptr \
                cpp_modernize.cpp
Niestety nie udało mi się zmusić do działania opcji "pass-by-value", które ma pozwolić na zadziałanie move sementic.
#include <iostream>
#include <memory>
#include <vector>
#include <map>

class ICar {
public:
    virtual ~ICar() {};
    virtual int engine(const std::string& name) = 0;
};

class Car : public ICar {
public:
-   virtual int engine(const std::string& name) {
+   virtual int engine(const std::string& name) override {
        std::cout << "Engine " << name << " start" << std::endl;
        return 44;
    }
};

std::vector<std::string> rewrite(const std::map<std::string, int> m) {
    std::vector<std::string> out;
-   for (std::map<std::string, int>::const_iterator it = m.cbegin(); it != m.cend(); ++it) {
+   for (auto it = m.cbegin(); it != m.cend(); ++it) {
        out.push_back(it->first);
    }

    return out;
}

int main() {
    std::map<std::string, int> m = {{"3", 3}, {"4", 4}, {"8", 8}};
    std::vector<std::string> v = rewrite(m);
-   std::auto_ptr<ICar> ferrari(new Car());
+   std::unique_ptr<ICar> ferrari(new Car());

    int oil_counter = 0;
-   for (int i = 0; i < v.size(); ++i) {
-       if (ferrari.get() != NULL) {
-           int result = ferrari->engine(v[i]);
+   for (auto & elem : v) {
+       if (ferrari.get() != nullptr) {
+           int result = ferrari->engine(elem);
            oil_counter += result;
        }
    }
    return 0;
}

24 grudnia 2014

[Defrag] procdump

Kolejny fajna porcja podcastów z Defrag dostępna na channel9, dotycząca narzędzia ProcDump (sysinternals).
ProcDump jest programem konsolowym do zrzucania dump-ów pamięci (zdolności takie posiada też TaskManager, czy Process Explorer, ale nie tak zaawansowane). To co w nim najfajniejsze to tworzenie dump-ów programów w wyniku wystąpienia określonych warunków (pojawienie się wyjątku, określone zużycie pracy procesora/pamięci). Warto zacząć do przeglądnięcia dostępnych przykładów:
procdump.exe -? -e
Trzeba pamiętać o podaniu nazwy pliku gdzie dump zostanie zrzucony, jakoś przykłady o tym nie wspominają, a komunikat zwrotny nie jest intuicyjny. Kilka użytecznych przełączników:
  • -mp, -ma - domyślnie procdump, tworzy mini-dump, czasami jest to wystarczające, ale jeżeli będzie potrzebna nam większa ilość pamięci, można skorzystać z opcji Mini-Plus (-mp), lub do zrzutu całej pamięci (-ma)
  • -r - opcja tworzy klon pamięci, dzięki czemu proces tworzenia dumpa, nie przerywa działania oryginalnego programu
  • -w - jeżeli proces jeszcze nie jest odpalony, poczekaj na niego
  • -e - zrzuć dump w razie wystąpienie wyjątku. Domyślnie zadziała, gdy nie obsłużona zostanie druga szansa na złapanie wyjątku. Np. program zgłosi wyjątku access violation, na który system nie będzie umiał zareagować, więc nastąpi crash aplikacji. W ten sposób w dump-ie pojawi się miejsce, z którego pierwszy wyjątek został złapany, a nie gdzie został rzucony. Aby reagować na pierwsze pojawienie się wyjątku można skorzystać z opcji "-e 1"
  • -f - filter na wyjątki i debug logi
Przykład użycia:
#include "stdafx.h"
#include <iostream>

int main() {
    std::cout << "Hello crash" << std::endl;
    int *ptr = new((int *)0xdeadbeaf) int(44);
    return 0;
}
C:\userdata\>procdump.exe -mp -e 1 -f "ACCESS_VIOLATION" -w ConsoleCrash.exe crash.dmp

ProcDump v7.1 - Writes process dump files
Copyright (C) 2009-2014 Mark Russinovich
Sysinternals - www.sysinternals.com
With contributions from Andrew Richards

Waiting for process named ConsoleCrash.exe...

Process:               ConsoleCrash.exe (3344)
CPU threshold:         n/a
Performance counter:   n/a
Commit threshold:      n/a
Threshold seconds:     10
Hung window check:     Disabled
Log debug strings:     Disabled
Exception monitor:     First Chance+Unhandled
Exception filter:      *ACCESS_VIOLATION*
Terminate monitor:     Disabled
Cloning type:          Disabled
Concurrent limit:      n/a
Avoid outage:          n/a
Number of dumps:       1
Dump folder:           C:\userdata\
Dump filename/mask:    crash


Press Ctrl-C to end monitoring without terminating the process.

[22:33:05] Exception: C0000005.ACCESS_VIOLATION
[22:33:05] Dump 1 initiated: C:\userdata\crash.dmp
[22:33:06] Dump 1 complete: 1 MB written in 0.5 seconds
[22:33:06] Waiting for dump to complete...
[22:33:07] Dump count reached.

23 grudnia 2014

[C++11] Elementy nowoczesnego C++

Kilka porad (http://herbsutter.com/elements-of-modern-c-style/) od Herba Shuttera dotyczące najistotniejszych elementów nowego C++.
  • Używaj auto, gdy tylko się da
  • Korzystaj z inteligentnych wskaźników i surowych wskaźników (nie mających prawa własności)
  • Stosuje nullptr. Nigdy 0 albo NULL
  • Korzystaj z "range for"
  • Korzystaj z wersji begin(), end() (free function) nie będącymi metodami klasy
  • Korzystanie z lambd wpływa na elegancję i szybkość kodu
  • Korzystaj z move semantic, zamiast kopiować obiekty
  • Uniform Initialization. Korzystaj z operatora przypisania (=) dla prostych typów oraz auto - po staremu. Dla wszystkich innych przypadków lepiej korzystać z {}. Nowa składnia chroni przed kilkoma starymi problemami, jak niepożądana konwersja, niezainicjowane zmienne lub przypadkowe stworzenie deklaracji funkcji.

22 grudnia 2014

[python] Beautiful Soup 4

Biblioteka do wyciągania danych z HTML-a i XML-a.
Różne ścieżki instalacji:
$ sudo apt-get install python-bs4
$ sudo apt-get install python3-bs4
$ pip install beautifulsoup4
BeautifulSoup zwraca obiekt reprezentujący dokument jako zagnieżdżone obiekty (kolejne pola) - każdy odpowiada prawdziwemu tag-owi. Najczęściej używane funkcji to find() i find_all(), pozwalające filtrować interesujące nas treści, ale istnieje też możliwość edytowanie struktury dokumentu.
soup = BeautifulSoup('<html>' \
                     '<a>' \
                        '<b>info1</b>' \
                        '<b>info2</b>' \
                     '</a>' \
                     '<c>text2</c>' \
                     '</html>')
print(soup.a.b.string)

tag = soup.find('a')
print(tag)
Wynik:
info1
<a><b>info1</b><b>info2</b></a>
Istnieje też możliwość tworzenia bardziej wyrafinowanych filtrów.
def filter_c_tag(tag):
    return tag.name == 'c' and tag.has_attr('item') and tag.attrs['item'] == 'bar'

soup = BeautifulSoup('<c item="bar">text1</c>' \
                     '<c item="foo">text2</c>' \
                     '<c item="bar">text3</c>')
tags = soup.find_all(filter_c_tag)
for t in tags:
    print(t.string)
Wynik
text1
text3

17 grudnia 2014

Upgrade do Ubuntu 14.10 bez Unity

Aktualizacja z konsoli, bez wykorzystania narządzie dostępnych w Unity.
Konfiguracja proxy, jest niestety skomplikowana, brak jest centralnego punktu, który mógłby propagować zmiany dla wszystkich programów w systemie. Dla większości programów wystarczy wyedytować plik /etc/environment. Dla apt-get należy stworzyć specjalny plik /etc/apt/apt.conf.d/95proxies.
$ sudo vim /etc/environment

http_proxy="http://example.com:8080/"
https_proxy="https://example.com:8080/"
ftp_proxy="ftp://example.com:8080/"
no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"
HTTP_PROXY="http://example.com:8080/"
HTTPS_PROXY="https://example.com:8080/"
FTP_PROXY="ftp://example.com:8080/"
NO_PROXY="localhost,127.0.0.1,localaddress,.localdomain.com"

# Dla apt-get
$ sudo touch /etc/apt/apt.conf.d/95proxies
$ sudo vim /etc/apt/apt.conf.d/95proxies

Acquire::http::proxy "http://example.com:8080/";
Acquire::ftp::proxy "ftp://example.com:8080/";
Acquire::https::proxy "https://example.com:8080/";
Upgrade do nowej wersji:
# Ustawienie Prompt=normal, jeżeli jest taka potrzeba
sudo vim /etc/update-manager/release-upgrades

# Instalacja narzędzia update-manager-core jeżeli jest to potrzebne
sudo apt-get install update-manager-core

# Właściwy upgrade
do-release-upgrade
Instalacja brakujących sterowników z płyty:
sudo mount /dev/cdrom /media/mydrivers/
sudo /media/mydrivers/run.sh

14 grudnia 2014

Migracja repozytorium svn do git-a

Nie jest to nawet takie skomplikowane. Dodatkowo, skonfigurowałem msysGit-a pod windows-em. Wymagało to wygenerowania kluczy i skonfigurowania kilku dodatkowych narzędzi (plink/pageant). Przydatne okazały się dwa poniższe linki:
Powtarzając częściowo za poradnikiem, zaczynamy od wygenerowania listy użytkowników svn-a, na tej podstawie tworzymy plik user.txt, w którym mapujemy ich na użytkowników git-a
$ svn log --xml | grep author | sort -u | perl -pe 's/.*>(.*?)<.*/$1 = /'
$ cat users.txt
kungfurry = Robert <robert@example.com>
Następnie klonujemy repozytorium (flaga -s umożliwia śledzenie gałęzi trunk/branch/tags, ale ja nie utrzymywałem tej konwencji i nie testowałem tej opcji). Poradnik zaleca też skasowanie jakiś śmieci z katalogu .git
$ git svn clone svn+ssh://kungfurry@example.com/svn/some_project \
      --authors-file=users.txt --no-metadata my_project
$ rm -Rf my_project/.git/refs/remotes
Wybrałem najprostszą z możliwych ścieżek, chodziło mi bowiem o przeniesienie się na git-a całkowicie. Z tego co już miałem stworzyłem gołego klona i wypchnąłem go na zdalny serwer. Po skasowaniu lokalnego repo, zrobiłem klona z serwera, w ten sposób automatycznie zostały stworzone odniesienie do zdalnego repozytorium.
$ git clone --bare my_project my_project.git
$ scp -r my_project.git kungfurry@example.com:/home/kungfurry/git_repos/
$ rm -rf my_project
$ git clone kungfurry@example.com:/home/kungfurry/git_repos/my_project.git

8 grudnia 2014

[python] pip i virtualenv

Fajny wpis na temat pip oraz virtualenv. Od wersji 3.4, pip jest instalowany razem z pythonem:
Paczki instalowane są standardowo w Lib\site-packages. Instalacja BeautifulSoup4 i sprawdzenie działania:
c:\Python34\Scripts>pip install beautifulsoup4
c:\>dir c:\Python34\Lib\site-packages\bs4\
c:\>python
>>> from bs4 import BeautifulSoup
virtualenv można wykorzystać do testowej instalacji paczek, jeżeli nie chcemy zaśmiecać sobie środowiska produkcyjnego.
mkdir project
cd project
virtualenv env
source env/bin/activate

pip install beautifulsoup4

6 grudnia 2014

md5 w python-ie

Obliczanie funkcji skrótu MD5, z pomocą standardowej biblioteki hashlib. Istnieje różnica między wersjami. Funkcja update() w wersji 2, przyjmuje jako argument string, w wersji trzeciej argument musi być interpretowany jako bufor bajtów:
Python 2:
# -*- coding: utf-8 -*-
import hashlib

s = 'jeżeli nie popełniłeś błędu tzn. że się niczego nie nauczyłeś'
print(type(s))

m = hashlib.md5()
m.update(s)
print(m.hexdigest())
Wyniki:
<type 'str'>
f9e58600ead7130b26fab4f1b943b79b
Python 3:
import hashlib

s = 'jeżeli nie popełniłeś błędu tzn. że się niczego nie nauczyłeś'
s = s.encode('utf-8')
print(type(s))

m = hashlib.md5()
m.update(s)
print(m.hexdigest())
Wyniki:
<class 'bytes'>
f9e58600ead7130b26fab4f1b943b79b

1 września 2014

[python] HTTP POST requests

Zadanie: jak wykonać zapytanie POST do strony. Dla testów skorzystałem z fajnej stronki http://requestb.in/, bo nie chciało mi się stawiać własnego serwera. Trzeba sobie za jej pomocą wygenerować link, który będzie służył nam do testów.
Najpierw standardowa biblioteka urllib2. W python-ie 3, została podzielona na dwie, urllib.request oraz urllib.error. W pierwszej kolejności przerabiamy parametry, które wstawimy do zapytania za pomocą urlencode() do "url-encoded" string (spacja na %20 itp.). Następnie tworzymy zapytanie url - request.Request(). Jeżeli zostanie dostarczony parametr data (jak w tym przypadku), to zapytanie będzie typu POST.
#! /usr/bin/env python3

import urllib.parse
import urllib.request

def post1():
    url = 'http://requestb.in/kcco1337'
    post_data = urllib.parse.urlencode({'login': 'user1', 'password': 'hasło 1'})

    binary_data = post_data.encode('utf-8')
    req = urllib.request.Request(url, data=binary_data)
    u = urllib.request.urlopen(req)
    print(u.read())
Raw body odebrane przez requestb.in:
login=user1&password=has%C5%82o+1
Urllib zbiera sporo krytyki, jako nieodpowiadający naszym czasom i trudny w obsłudze (muszę się z tym troszkę zgodzić). Alternatywę, którą znalazłem jest biblioteka requests, której działania nie muszę nawet opisywać.
#! /usr/bin/env python3

import requests

def post2():
    url = 'http://requestb.in/kcco1337'
    r = requests.post(url, data={'login': 'user1', 'password': 'hasło 2'})
    print(r.status_code)
    print(r.content)
Jedyną rzeczą, której nie jestem pewny jest kodowanie, najwyraźniej jednak w tym przykładzie wszystko poszło ok. Odebrane raw body:
password=has%C5%82o+2&login=user1

31 sierpnia 2014

[python] mock_open - testowanie uchwytu do pliku

Coś czego brakowało mi podczas pisania skryptów, czyli metody na zmockowanie bibliotecznej funkcji open (do otwierania plików). Framework do mocków posiada specjalną metodę mock.mock_open(), jednak wszystkie przykłady jakie znalazłem na sieci pokazywały działanie tego mechanizmu w interpreterze, co jakoś nie współgrało z moimi TestSuite-ami. Wydaje mi się, że da się to rozwiązać w bardziej elegancji sposób, ale póki co jest to coś co działa.

Przykład: zapisywanie i odczytywanie z pliku:
#! /usr/bin/env python3
import os

def fileWrite(fileName):
    f = open(fileName, 'w+')
    f.write('text\n')
    f.close()

def fileRead(fileName):
    f = open(fileName, 'r')
    count = 0
    for _ in f:
        count += 1
    f.close()
    return count
Po pierwsze nie udało mi się skorzystać z patch za pomocą dekoratora, a jedynie przez with. Także wywołanie jest troszkę inne, jako drugi parametr, przekazywany jest mock, stworzony przez mock.mock_open() (zamiast domyślnie wygenerowanego z MagicMock). Parametr create odpowiada za wygenerowanie atrybutów, jego brak sprawia, że zgłaszany jest brak atrybutu asdf.open() przez test. Tego jeszcze nie ogarniam.
W test_filterWrite() zawołanie mocka m, wygeneruje handler - czyli zadziała jak open(), które zwiększy licznik wywołań, dlatego można z tego skorzystać, dopiero po sprawdzeniu asercji (linijka 14).

Jeżeli potrzebujemy aby z uchwytu można było pobrać dane (przykład test_fileRead), istnieje alternatywne rozwiązanie z przesłonięciem __enter__ oraz __iter__. Nie zwiększa to licznika open i assert w linijce 27, będzie działał poprawnie.
import unittest
from unittest import mock
from unittest.mock import mock_open
import asdf

class TestFindFile(unittest.TestCase):
    def test_fileWrite(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            asdf.fileWrite(fileName)

            m.assert_called_once_with(fileName, 'w+')
            file_handler = m()
            file_handler.write.assert_called_once_with('text\n')
            file_handler.close.assert_called_once_with()

    def test_fileRead(self):
        m = mock_open()
        with mock.patch('asdf.open', m, create=True):
            fileName = 'aaa.txt'
            file_handler = m.return_value.__enter__.return_value
            file_handler.__iter__.return_value = ['line1', 'line2']

            self.assertEqual(2, asdf.fileRead(fileName))

            m.assert_called_once_with(fileName, 'r')
            file_handler.close.assert_called_once_with()

29 sierpnia 2014

python "with"

Chociaż w samym języku "with" jest już od dawna, dopiero teraz się z nim mierze. with umożliwia bezpieczne zarządzanie obiektem (po opuszczeniu bloku, zwalnia zasoby, które przy wejściu były zarezerwowane). W klasycznym przykładzie z open(), with otwiera plik i zwraca uchwyt do niego (przez as), który zostanie zwolniony po opuszczeniu bloku (automatycznie zostanie zawołane f.close()).
with open("aaa.txt") as f:
    data = f.read()
Mechanizm ten można stosować, gdy obiekt, który przekażemy do with posiada metody __enter__() (która będzie wołana na wejściu do bloku, oraz metodę __exit__() (która zostanie zwołana przy wyjściu z bolku). Jeżeli podczas wykonywania zostanie rzucony jakiś wyjątek, w pierwszej kolejności zostanie zwolniony zasób, a następnie wyjątek zostanie wyrzucony wyżej. Tutaj dwa linki, które dokładniej wyjaśniają wszystkie zawiłości.

28 sierpnia 2014

[C++] RAII - Resource Acquisition Is Initialization

Notatka z wykładu Michaela Caissea "Introduction to Modern C++ Techniques" na C++Now (dawniej BoostCon), dotycząca wzorca projektowego RAII. Choć mam okazję korzystać z jej dobrodziejstw przy okazji boost-a, oraz biblioteki standardowej to jednak sam nigdy nic nie napisałem. Technika polega na przyjęciu zasobu, podczas konstruowania obiektu i zwalniania podczas jego destrukcji. Programista nie musi pamiętać o wykonaniu dodatkowych czynności, gdy zasób już nie będzie potrzebny, zwalnianiem zajmie się kompilator.
Przykład poniżej pokazuje zarządzeniem uchwytem do pliku (w konstruktorze plik jest otwierany, w destruktorze niszczony), za pomocą funkcji znanych z C.
#include <iostream>
#include <string>
#include <cstdio>

class FileManager {
public:
    FileManager(const std::string& filePath) {
        std::cout << "Open file" << std::endl;
        _fileHandler = std::fopen(filePath.c_str(), "w+");
    }

    void write(const std::string& str) {
        if(_fileHandler == nullptr)
            return;

        if(std::fputs(str.c_str(), _fileHandler) == EOF)
            throw std::exception();
    }

    ~FileManager() noexcept {
        std::cout << "Close file" << std::endl;
        if(_fileHandler != nullptr)
            std::fclose(_fileHandler);
    }

private:
    FILE* _fileHandler;
};

void modifyFile() {
    FileManager f("aaa.txt");
    f.write("some text\n");
}

int main() {
    modifyFile();
    std::cout << "End" << std::endl;
    return 0;
}
Wynik:
Open file
Close file
End

27 sierpnia 2014

[python] mockowanie iterowanych obiektów pętli

Problem, który wciąż do mnie powracał. Jak zmockować funkcję (generator), wołaną z pętli for. Ponieważ taka funkcje może zwrócić kilka wyników, cały czas próbowałem zaprząc do działania "side_effect" - bezskutecznie. Myślałem, że jej działanie jest podobne do generatora, cóż znów się czegoś nauczyłem. Pętla wymaga aby na obiekcie (z którego pobierane są dane) dało się iterować, dlatego "return_value" zwracające np. listę rozwiązuje problem. "side_effect" nadaje się bardziej do mockowania zachowań wewnątrz pętli.

Przykład z os.walk(), która zwraca trzy elementową krotkę:
import os

def findFile(fileName):
    for root, dirs, files in os.walk('~/'):
        if fileName in files:
            return os.path.join(root, fileName)

    return None
Test:
import unittest
import mock
from mock import call
import asdf

class TestFindFile(unittest.TestCase):
    @mock.patch('asdf.os')
    def test_findFIle(self, mockOs):
        fileName = 'aaa.txt'

        mockOs.walk.return_value = [('/home/user',        ('folder',),      ('bbb.txt',)),
                                    ('/home/user/folder', ('fol1', 'fol2'), (fileName,))]
        mockOs.path.join.return_value = '/home/user/folder' + fileName

        self.assertEqual('/home/user/folder' + fileName, asdf.findFile(fileName))

        mockOs.walk.assert_called_once_with('~/')
        mockOs.path.join.assert_called_once_with('/home/user/folder', fileName)

17 sierpnia 2014

8 sierpnia 2014

[C++11] nullptr

W C/C++ przyjęło się oznaczać pusty wskaźnik przez NULL, jednak identyfikator ten zostanie rozwinięty i tak do 0. Może to doprowadzić do pewnej niejednoznaczności, gdy posiadamy dwie przeciążone funkcje:
int foo(int);
int foo(char *);
Jeżeli NULL zostanie rozwinięte do 0L (ponieważ standard nie gwarantuje, że będzie to 0 - 32bity), to w takim przypadku nie mamy pewności, która metoda zostanie zawołana. Nowy standard wprowadza nowe słowo kluczowe nullptr (będące typu std::nullptr_t). Stała ta nie może być przypisywana ani porównywana z typami całkowitymi, może jednak porównywać ją z dowolnym typem wskaźnikowym.

Źródła:

7 sierpnia 2014

[Windows] Defrag Toos - sysinternals konfiguracja

Zbiór fajnych podcastów na temat diagnozowania problemów w Windowsie, za pomocą narzędzi sysinternals. Aby uzyskać lepszą diagnostykę niezbędna jest instalacja "Microsoft Windows SDK for Windows 7 and .NET Framework 4", w którym to znajduje się się lepsza wersja dbghelp.dll.
Oczywiście nie obyło się bez problemów. Po pierwsze, trzeba było odinstalować "Microsoft Visual C++ 2010 x86 Redistributable", aby w ogóle doprowadzić do instalacji (doprowadziły mnie do tego fail logi).
Po drugie nie wszystkie narzędzie z SDK rozpakowują się tam, gdzie to zostało wskazane, większość i tak wędruje do Program Files.

Interesują nas narzędzie dostępne w "Redistributable Packages" (czyli Microsoft Visual C++ 2010, Application Verifier, Debbuging Tools, Windows Performance Toolkit). Jednak SDK ściągnie jedynie instalatory. Nas interesują instalatory debbugera dla wersji 32 i 64 bitowej.
c:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\dbg_amd64.msi
c:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\dbg_x86.msi
Można je zainstalować w dowolnej lokalizacji. Każda posiada odpowiednią wersję dbghelp.dll, która będzie nam potrzebna - można ją nawet przekopiować na USB (diagnostyczny) z innymi narzędziami sysinternals.

Niektóre z narzędzi (jak Process Explorer) potrafią pokazywać jakie biblioteki dll, są załadowane przez dany proces do pamięci. Przydać się mogę symbole debbugjące dostępne do Microsoftu. Ścieżki, gdzie będą przechowywane, można ustawić za pomocą poniższego skryptu. Ważne są też zmienne środowiskowe (które zdaje się, są również wykonywane przez inne tego typu programy). Magiczne SRV, z tego co zrozumiałem, ustawia przeszukiwanie najpierw w lokalnym katalogu, a jeżeli ich tam nie znajdzie, ściąga symbole z serwera i robi ich backup w lokalnym katalogu (działanie magicznych gwiazdek).

Symbols.cmd:
md c:\My
md c:\My\Src
md c:\My\Sym
md c:\My\SymCache
setx /M _NT_SOURCE_PATH SRV*C:\My\Src
setx /M _NT_SYMBOL_PATH SRV*C:\My\Sym*http://msdl.microsoft.com/download/symbols
setx /M _NT_SYMCACHE_PATH C:\My\SymCache
pause

Drugi skrypt podawany przez autorów, służy do drukowania większej ilości detali (jeżeli dobrze zrozumiałem) - nie testowałem. Informacje na stronie mówią, że może to spowolnić wczytywanie symboli, więc warto włączać flagę tylko, gdy jest to uzasadnione.

DbgHelp_Logging.cmd:
rem msdn.microsoft.com/en-us/library/windows/desktop/ms680687.aspx
md c:\My
md c:\My\DbgHelp
setx DBGHELP_DBGOUT 1 
setx DBGHELP_LOG C:\My\DbgHelp\DbgHelpLog.txt
pause

6 sierpnia 2014

clang-format

clang-format.py umożliwia zmianę formatowania pliku (C++) według przyjętej w danym projekcie konwencji. Pokrótce omówił to narzędzie Chandler Carruth w swoim wykładzie na GoingNative 2013 (25 minuta):
Potrafi on korzystać z kilku wbudowanych styli: Google, WebKit, llvm. Możliwe jest także skorzystanie z własnego, za pomocą przełącznika -style=file. Styl taki zostanie zaczytany z pliku .clang-format, który musi znajdować się gdzieś w ścieżce powyżej wywołania. Własny styl można zbudować, na podstawie już istniejącego, najpierw zrzucając oryginał (--dump-config) go pliku, a następnie modyfikując.
# użyj wbudowanego stylu
clang-format-3.5 -style=WebKit -i main.cpp

# zrzuć styl Google do pliku .clang-format
clang-format-3.5 -style=Google --dump-config > .clang-format

# skorzystaj z własnego stylu
clang-format-3.5 -style=file -i main.cpp
Działanie formatera, można zautomatyzować np. w QtCreatorze. Jedna uwaga, wszelkie modyfikacje w:
"Tools | Options... | Environment | External Tools | Add | Add Tool", kasują skrót klawiaturowy, który został podpięty dla takiego formatera.

5 sierpnia 2014

[gcc + tutorial] x86 Assembly

Zobaczyłem fajny przełącznik (-masm) do gcc, który wyrzuca kod asemblera bardziej przypominający MASM niż NASM.
gcc main.cpp -S -masm=intel
Tutorial do asemblera x86:

4 sierpnia 2014

4 lipca 2014

[Notki] Praca w terminalu z tmux-em.

Bardzo fajny artykuł Łukasza Wróbla na temat pracy z tmux-em. Fajne narzędzie do podziału terminala na kilka paneli. Nie jest to wciąż coś czego poszukuje, ale na razie wystarcza:
Po wciśnięciu Ctrl+b program przechodzi do trybu kontroli. Notki:
  • Ctrl+b " - split horyzontalnie
  • Ctrl+b % - split wertykalnie
  • Ctrl+b strzałka - przełączenie między panelami
  • Ctrl+b+strzałka - zmiana rozmiaru panelu
  • Ctrl+b PgUp/PgDn - skrolowanie w panelu (aby wyłączyć ten tryb "q")

29 czerwca 2014

[python] unittest/mocks - część II

Garść informacji na temat unittest/mock, które sobie ostatnie przyswoiłem, m.in. czytają ciekawy artykuł:

Dopasowanie czegokolwiek - mock.ANY

Odpowiednik testing::_ w GoogleMock, czyli akceptowanie każdego argumentu z jakim zostanie wywołany mock, co pozwala na rozluźnienie zależności (coupling).
class Gearbox:
    def moveGear(self, gearLevel):
        print('Gear to level: ' + str(gearLevel))
Test
class TestVehicle(unittest.TestCase):

    def test_tractorStartOnRandomGear(self):
        mock_gearbox = mock.create_autospec(Gearbox)
        tractor = Vehicle(mock_gearbox)
        tractor.runEngine()
        mock_gearbox.moveGear.assert_called_once_with(mock.ANY)

Mock vs. MagicMock vs. create_autospec

Z tego co udało mi się zrozumieć MagicMock-i, różnią się od zwykłych Mock-ów tym, że posiadają predefiniowane wartości dla pewnych pospolitych metod np.: __len__ == 1.
Ponieważ mock.Mock i mock.MagicMock akceptują wołania z dowolną ilością argumentów, najlepiej korzystać mock.create_autospec. Taki mock w przypadku gdy ilość argumentów się nie zgadza rzuci wyjątek.

Dekorator @mock.patch zamiast dependency injection

Dekoratory z biblioteki mock, pozwalają na zgrabne przesłanianie klas i modułów w testowanym kodzie, dzięki czemu korzystamy z dobrodziejstw dependency injection, bez niepotrzebnego rozbudowywania "konstruktorów".

Kolejność dekoratorów jest istotna

Każdy moduł importuje inne moduły do swojej lokalnej przestrzeni, dlatego należy pamiętać, by nie mock-ować modułów (np. os) w pliku z testami (bo tylko tutaj będzie zmock-owany). Należy explicit wskazać na moduł gdzie jest nasz testowany kod (np. mycode.os).

Kiedy korzysta się z wielu dekoratorów należy pamiętać o ich kolejności. Mapowanie dekoratorów do parametrów następuje w odwrotnej kolejności niż deklaracje. Przykład poniżej.
import os
import sys

class FilesSystem:
    def countFolders(self):
        l = [dir for dir in os.listdir('.') if os.path.isdir(dir)]
        sys.exit(len(l))
Test
import unittest
import mock
import asdf

class TestFileSystem(unittest.TestCase):

    @mock.patch('asdf.os')
    @mock.patch('asdf.sys')
    def test_countFolders(self, mock_sys, mock_os):
        mock_os.listdir.return_value = ['folder1', 'folder2']
        mock_os.path.isdir.return_value = True

        fs = FilesSystem()
        fs.countFolders()

        mock_os.listdir.assert_called_once_with('.')
        expected = [call.mock_os.path.attribute.isdir('folder1'),
                    call.mock_os.path.attribute.isdir('folder2')]
        self.assertTrue(mock_os.path.isdir.mock_calls == expected)
        mock_sys.exit.assert_called_once_with(2)
Innym rodzajem dekoratora jest @mock.patch.object, który pozwala na z-mockowanie wybranej metody z klasy, gdy cała reszta nadal będzie wołała oryginalną implementację.
class Accounts:
    def isUser(self, user):
        print('isUser ' + user)

    def assign(self, user, movie):
        print('assign ' + user + ' ' + movie)
Test
class TestVideoLib(unittest.TestCase):

    @mock.patch.object(Accounts, 'isUser')
    def test_whenUserExist_thenHeCouldRentAMovie(self, mock_isUser):
        mock_isUser.return_value = True
        accounts = Accounts()
        videoLib = VideoLib(accounts)
        videoLib.rent('Jhon', 'Titanic')

        mock_isUser.assert_called_once_with('Jhon')

Otwarte kwestie: Odpowiednik StrictMock, mock-owanie metody open, list comprehension - które niekiedy sprawa mi problemy, gdy chce jest otestować.

28 czerwca 2014

[Notki] Działanie i budowanie plików dll

Notki z godzinnego podcastu Gynvaela Coldwinda odnośnie budowania oraz działania plików dll. Tutaj tylko budowanie.



Plik asdf.c.
#include <stdio.h>

__declspec(dllimport) int add(int a, int b);

int main(void) {
    printf("5+6 ==  %d\n", add(5, 6));
    return 0;
}
Biblioteka (my_dll.c) będzie udostępniać tylko jedną funkcję. __declspec nie jest konieczne, jeżeli będziemy korzystać z pliku z definicjami.
__declspec(dllexport) int add(int a, int b) {
    return a + b;
}
Plik z definicjami my_dll.def.
LIBRARY my_dll
EXPORTS
    add
    dodaj=add
Budowanie:

Najpierw kompilujemy do pliku obiektowego, następnie trzeba posłużyć się dllwrap, który jest rodzajem linkera, dla plików dll. Dodawanie pliku z definicjami nie jest obowiązkowe, przydaje się dopiero wtedy, gdy chcemy stworzyć aliasy na nasze funkcje.

dlltool pozwala na tworzenie bibliotek (dla kompilatorów) (.a), coś z czym będziemy linkować dany program. Jest to konieczne, bowiem nie każdemu kompilatorowi możemy powiedzieć, aby korzystał z dll, akurat gcc to potrafi. W tej bibliotece będzie wyłącznie informacja dla linker, że dany kod znajduje się w jakiejś dll-ce. Wymagany jest plik z definicjami exportu.
> gcc -c my_dll.c
> dllwrap my_dll.o -o my_dll.dll --def my_dll.def 
> dlltool -l libmy_dll.a -D my_dll.dll -d my_dll.def

> gcc asdf.c my_dll.dll
# alternatywne rozwiązanie z wskazaniem biblioteki libmy_dll.a 
# będzie poszukiwana w obecnym katalogu
> gcc asdf.c -L. -lmy_dll

Name mangling

Funkcje stworzone w C++, są exportowane pod innymi nazwami np. _Z4addii - udekorowane są dodatkowymi informacjami. Wynika to z faktu, że C++ pozwala na istnieje kilku funkcji o tej samej nazwie, pod warunkiem, że różnią się one przyjmowanymi typami. Aby móc w kodzie C++ skorzystać z funkcji add (funkcji C z dll) należy skorzystać z takiej deklaracji:
extern "C" {
    int add(int a, int b);
}
W odwrotnej sytuacji, gdy mamy kod w C i chcemy skorzystać z funkcji C++ w dll, trzeba posłużyć się tą udekorowaną nazwą _Z4addii.

26 maja 2014

5 maja 2014

Flaw of Software Design

Seria krótkich wykładów Maxa Kanata-Alexandera, dotycząca problemów jakie pojawiają się podczas tworzenia oprogramowania:
  • Writing Code that isn't Needed
  • Zasada podobna do YAGNI (You aren't gonna need it), czyli nie tworzenie kodu, który nie jest nam potrzebny i usuwania kodu, który nie jest już wykorzystywany.
  • Not Making the Code Easy to Change
  • Najczęściej ten problem, pojawia się przez zbyt wielką liczbę założeń w projekcie, co do tego jak nasz projekt będzie działał w przyszłości, lub przez pisanie kodu bez poświęcenie wystarczająco czasu na projektowanie. Jednym z częstych błędów jest np. przyjęcie założenia, że nasz kod, nie będzie wspierał wielu języków, albo nie wykorzysta frameworka do unit testów. Jeżeli tylko wiemy o konieczności w przyszłości dodania jakiejś zmiany, powinniśmy dodać bardzo minimalny interfejs, który to ułatwi (moim zdaniem troszkę, kłóci się to z pierwszą zasadą).

    "Kod powinien być projektowany, na podstawie wiedzy, którą mamy obecnie, nie na podstawie tego co myślimy, że będzie działo się w przyszłości"
  • Being Too Generic
  • Czyli over-engineering, coś co się przydarza często doświadczonym deweloperom. Próba tworzenia generycznych rozwiązań, które niekoniecznie są potrzebne już w tej chwili, gdy mniej generyczne rozwiązani są szybsze do implementacji i szybciej przynoszą wartość klientowi.

    "Bądź tak generyczny, jak tego potrzebujesz w tej chwili"
  • Incremental Development & Design
  • Za każdym razem, gdy chcesz dodać coś nowego do systemu, rezultat ma być taki jakby zostało to już na początku zaprojektowane do takiego działania. Wymaga to dużo refactoringu. Przystępując do pracy nad projektem najważniejszą kwestią jest to by tworzyć wszystko we właściwej kolejności, zacząć od najbardziej istotnych elementów systemu np. funkcja odtwarzania wideo, przed listą wyboru plików do odtworzenia.

8 marca 2014

Windows 7 - odzyskiwanie miejsca na dysku

Microsoft wydał w październiku zeszłego roku (2013) poprawkę (KB2852386) do swojego narzędzie cleanmgr, pozwalającą na usunięcie kopii wszystkich aktualizacji, nawet tych zamienionych przez nowsze wersje. Poprawka pozwala na usunięcie tych starych wersji i zwolnienie całkiem sporej ilości miejsca na dysku (u mnie 10GB).

Najpierw sprawdzenie, czy aktualizacja jest dostępna w systemie.
PS C:\Users\beru> get-hotfix -id KB2852386

Source        Description      HotFixID      InstalledBy          InstalledOn              
------        -----------      --------      -----------          -----------              
BERU-KOMP...  Update           KB2852386     ZARZĄDZANIE NT\SY... 2013-10-09 00:00:00      
W przypadku braku, trzeba ją zainstalować ręcznie:
Następnym krokiem jest uruchomienie cleanmgr (Start | Akcesoria | Narzędzia systemowe | Oczyszczanie dysku/Disk Cleanup), koniecznie z prawami administratora, tylko wtedy pojawi się opcja do "Windows Update - oczyszczanie"/"Windows Update Cleanup".

22 lutego 2014

18 lutego 2014

[C++11] Copy control i move semantic

Kolejny zbiór nowości w standardzie, z którymi nie do końca czuje się jeszcze pewnie. Myślę, że do tematu jeszcze powrócę, jak tylko stanie się on dla mnie bardziej klarowny.

Copy control


Konstruktor kopiujący

W przypadku konstruktora kopiującego parametr jest prawie zawsze referencją na const. Rzadko zdarza się sytuacja, abyśmy byli zmuszeni do modyfikacji obiektu, z którego kopiujemy.
struct Foo {
    Foo(const Foo&);
};

Zasada trzech/pięciu/zero (the rule of three/five/zero).

Istnieją trzy (pięć - w nowym standardzie) podstawowe operacje kontrolujące kopiowanie obiektu:
  • konstruktor kopiujący
  • operator przypisania
  • destruktor
  • move konstruktor
  • operator przeniesienia (move)
Nie ma wymagań by tworzyć wszystkie z nich, jednak powinny być one traktowane jako całość. Bardzo rzadko zdarza się potrzeba stosowania tylko jednej konstrukcji z pominięciem reszty. Sam Stroustrup zaleca by przy deklaracji chociaż jednej z operacji, stworzyć wszystkie pięć. W przeciwnym razie klasa nie powinna zawierać żadnej z tych konstrukcji - "the rule of zero".
Jeżeli nie chcemy użyć któregoś z mechanizmów, poniżej wymieniono kilka zasad, którymi należy się kierować:
  • Jeśli klasa wymaga destruktora, prawie na pewno wymaga konstruktora kopiującego i operatora przypisania
  • Jeśli klasa wymaga konstruktora kopiującego, prawie na pewno wymaga operatora przypisania i vice versa
  • Klasa która nie może być kopiowana powinna definiować konstruktor kopiujący i operator przypisania jako delete, zamiast tworzyć te dwie definicje prywatne
Tworząc operator przypisania należy pamiętać o dwóch rzeczach:
  • Operator przypisania (ma to też zastosowanie dla operatora move) musi pracować poprawnie, gdy próbujemy przypisać do siebie ten same element. Ma to szczególne znaczenie jeżeli, obiekt posiada dane zapisane w pamięci dynamicznej, a w wyniku przypisania tworzymy w pamięci nowe dane i kasujemy stare. W takim przypadku dobrą praktyką jest skopiowanie wartości po prawej stronie do zmiennej lokalnej, zniszczenie wartości stojącej po lewej i w końcu przypisanie jej wartości tymczasowej. [C++Primer, s. 512; s. 536]
  • Większość operatorów przypisania współpracuje z destruktorem i konstruktorem kopiującym
Storage& operator=(const Storage& r) {
    auto tmp = new string(*r.data);
    delete data;
    data = tmp;

    return *this;
}
W przeciwieństwie do mechanizmów copy-control swap nigdy nie jest wymagany, jednakże zdefiniowanie własnej metody, może być ważną optymalizacją dla klasy, która alokuje zasoby. Operatory przypisania, które korzystają z techniki "copy and swap", są automatycznie odporne na wyjątki i obsługują samoprzypisanie.
Należy pamiętać tylko o jednej rzeczy. Nigdy nie wolno wołać bezpośrednio std::swap(). Wymusza to bowiem korzystanie z wersji bibliotecznej swap(), tymczasem klasa może mieć zmienną, która posiada własną wersję tej funkcji. Najlepiej by wyboru dokonał kompilator, przez wciągnięcie przestrzeni nazw.
inline void swap(Storage &l, Storage& r) {
    using std::swap;
    swap(l.data, r.data);
    swap(l.counter, r.counter);
}

void swap(Counter &l, Counter &r) {
    Clock *tmp = l.clock;
    l.clock = r.clock;
    r.clock = l.clock;
}
Jeżeli klasa posiada chociaż jeden z mechanizmów tj.: konstruktor kopiujący, operator przypisania lub destruktor kompilator nie stworzy nam konstruktora i operatora move!

Move semantic


Najtrudniejsza rzecz, jaka przyszła mi do opanowania wraz z nowym standardem, czyli move semantic. Kilka linków:
Ponieważ kompilatory mogą optymalizować tworzenie zmiennych tymczasowych (copy elision), które służą tylko do zainicjowania innych zmiennych wyłączyłem tą opcję. (
g++ -std=c++11 -fno-elide-constructors main.cpp

std::move i std::forward

Ciekawie o nich opowiedział Scott Meyers w swoim wykładzie An Effective C++11/14 Sampler na GoingNative 2013, z czego sporo zaczerpnąłem. Żadne z mechanizmów niczego nie obiecuje, ich zadaniem jest jedynie rzutowanie (podczas kompilacji) do rvalue:
  • std::move (rzutowanie bezwarunkowe) - przekazuje obiekt jako rvalue
  • std::forward (rzutowanie warunkowe) - przekazuje obiekt jako rvalue, tylko jeżeli oryginalny obiekt był rvalue inaczej będzie to lvalue
std::move nie gwarantuje tego że coś zostanie przesunięte. Np. jego działanie na obiekcie const spowoduje jego skopiowanie. Korzystając z tego mechanizmu trzeba być bardzo ostrożnym ponieważ kompilator raczej nie poinformuje nas gdy std::move nie będzie przesuwać ("rozświetliło" by to bibliotekę standardową).

Korzystając z std::move obiecujemy, że nie będziemy korzystać już z danego obiektu chyba, że zamierzamy do niego coś przypisać lub go zniszczyć. Po wykonaniu std::move nie mamy żadnej gwarancji co do wartości przesuniętego obiektu.

Głównym zadaniem std::forward jest przekazanie argumentu (rvalue, lvalue, z const-em lub nie) w niezmienionej formie, do innej funkcji. Jeśli oryginalny obiekt, który jest wstawiony do std::forward był rvalue, zrobi z niego rvalue, a jeżeli lvalue, to lvalue - dlatego właśnie jest warunkowy.
void process(Storage& lvalue) {
    cout << "By lvalue reference" << endl;
}

void process(Storage&& rvalue) {
    cout << "By rvalue reference" << endl;
}

template <typename T>
void log(T&& param) {
    process(std::forward<T>(param));
}

int main() {
    Storage st;
    log(st);                     // przekazanie jako lvalue
    log<Storage>(std::move(st)); // przekazanie jako rvalue
}
Wynik:
Default constr.:  defaultData
By lvalue reference
By rvalue reference
Destructor:       defaultData

"Jeżeli oczekujesz szybkiego działania przekazuj przez wartość"

Zalecenia do poprzednich wersji standardu sugerowały by lvalue były opatrzone modyfikatorem const. Jest to już nieprawdą, bowiem powodowało by to kopiowanie obiektu. Jeżeli chcemy coś przesunąć, const nam w tym przeszkodzi.
void process(const Storage st) {
    Storage my = std::move(st); // Kopiowanie!
}

int main() {
    Storage st;
    process(st);
}
Wynik
Default constr.:  defaultData
Copy constructor: defaultData
Copy constructor: defaultData
Destructor:       defaultData
Destructor:       defaultData
Destructor:       defaultData

noexcept dla operacji move

Ponieważ z założenia operacje "move konstruktora" i "move operatora" kradną zasoby i nie alokują żadnych nowych, więc nie powinny też rzucać żadnych wyjątków. Powinniśmy poinformować o tym kompilator stosując noexcept. Co więcej, kontenery takie jak std::vector, sprawdzają, czy "move konstruktor" jest noexcept (podczas realokacji), jeżeli nie, zostanie zawołany zwykły konstruktor kopiujący.
#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Verbose {
    Verbose(std::string) { cout << "Constructor" << endl; }
    Verbose(const Verbose&) { cout << "Copy" << endl; }
    Verbose(Verbose &&) /*noexcept*/ {
        cout << "Move" << endl;
    }
    Verbose& operator =(const Verbose&) { cout << "assign=" << endl; }
    Verbose& operator =(Verbose&&) noexcept { cout << "move=" << endl; }
    ~Verbose() noexcept { cout << "Destructor" << endl; }
};

int main() {
    std::vector<Verbose> v;
    v.emplace_back("aaa");
    v.emplace_back("bbb");
}
Wynik:
Constructor
Constructor
Copy
Destructor
Destructor
Destructor

Uboczne działania destruktora

Move semantic niesie jeszcze jedno zwodnicze działanie, o którym należy pamiętać. Nigdy nie mamy pewności kiedy zostanie zawołany destruktor obiektu, z którego przenosimy zasoby. Jeżeli zdefiniowaliśmy własny destruktor, który ma jakieś działania uboczne, należy pamiętać o odpaleniu tego samego kodu w konstruktorze i operatorze move.

Przykłady


Pora na przykład, który pokazuje różne scenariusze, z którymi mamy do czynienia kopiując bądź przesuwając obiekty. Na początku "gadatliwa" klasa, która pokazuje, który z mechanizmów copy-control został użyty.
#include <iostream>
#include <string>
#include <vector>
#include <functional>

using namespace std;

struct Storage {
    Storage() : data("defaultData") {
        cout << "Default constr.:  " << data << endl;
    }
    Storage(std::string str) : data(str) {
        cout << "Constructor:      " << data << endl;
    }
    Storage(const Storage& st) : data(st.data) {
        cout << "Copy constructor: " << st.data << endl;
    }
    Storage(Storage&& st) noexcept : data(std::move(st.data)) {
        st.data = "old " + data + ", data was moved from here";
        cout << "Move constructor: " << data << endl;
    }
    Storage& operator=(const Storage& st) {
        if (&st != this) {
            cout << "operator= copy:   " << data << " := " << st.data << endl;
            data = "old " + data + ", now assigned " + st.data;
        } else {
            cout << "operator= copy:   " <<  data << " (this)" << endl;
        }
        return *this;
    }
    Storage& operator=(Storage&& st) noexcept {
        if (&st != this) {
            cout << "operator= move:   " << data << " := " << st.data << endl;
            string old_copy = data;
            data = std::move(st.data);
            st.data = "old " + data + ", data was moved from here";
            data = "old " + old_copy + ", now assigned " + data;
        } else {
            cout << "operator= move:   " << data << " (this)" << endl;
        }
        return *this;
    }
    ~Storage() noexcept {
        cout << "Destructor:       " << data << endl;
    }

    std::string data;
};
W przykładach simpleExample5 i simpleExample6, rzutujemy (std::move) wartość do rvalue reference - jest to referencja, więc stare dane zostają na miejscu (nic nie jest przesuwane, ani kopiowane).
void simpleExample1() {
    Storage st("val_1");
    st = st;
}

void simpleExample2() {
    Storage st1("val_1");
    Storage st2("val_2");
    st1 = st2;
}

void simpleExample3() {
    Storage st1("val_1");
    Storage st2("val_2");
    st1 = std::move(st2);
}

void simpleExample4() {
    Storage st1("val_1");
    Storage st = std::move(st1);
}

void simpleExample5() {
    Storage st1("val_1");
    Storage&& st = std::move(st1);
}

void simpleExample6() {
    Storage&& st1 = std::move(Storage("val_1"));
}

int main() {
    cout << "simpleExample1():" << endl;
    simpleExample1();
    cout << "simpleExample2():" << endl;
    simpleExample2();
    cout << "simpleExample3():" << endl;
    simpleExample3();
    cout << "simpleExample4():" << endl;
    simpleExample4();
    cout << "simpleExample5():" << endl;
    simpleExample5();
    cout << "simpleExample6():" << endl;
    simpleExample6();
}
Wynik dla simpleExample*:
simpleExample1():
Constructor:      val_1
operator= copy:   val_1 (this)
Destructor:       val_1

simpleExample2():
Constructor:      val_1
Constructor:      val_2
operator= copy:   val_1 := val_2
Destructor:       val_2
Destructor:       old val_1, now assigned val_2

simpleExample3():
Constructor:      val_1
Constructor:      val_2
operator= move:   val_1 := val_2
Destructor:       old val_2, data was moved from here
Destructor:       old val_1, now assigned val_2

simpleExample4():
Constructor:      val_1
Move constructor: val_1
Destructor:       val_1
Destructor:       old val_1, data was moved from here

simpleExample5():
Constructor:      val_1
Destructor:       val_1

simpleExample6():
Constructor:      val_1
Destructor:       val_1
W przykładzie returnValue1, kompilator sam się domyśli, że stworzona lokalnie (w ret()) zmienna zaraz zostanie zniszczona, więc zamiast korzystać z operatora przypisania (jak to było dawnej) zoptymalizuje tą operację przesuwając stworzoną zmienną lokalną. Ponieważ wyłączona jest optymalizacja "copy elision", widać dwa przesunięcia. Pierwsze przesuwa lokalną zmienną do zmiennej na stosie, drugie ze zmiennej na stosie do docelowej zmiennej st.

W drugim przypadku (returnValue2), mamy podobną sytuację, z tą różnicą, że zamiast "move operatora" działa "move konstruktor". Gdyby "copy elision" było włączone, kompilator prawdopodobnie wyciągnąłby wywołanie z tak prostej funkcji jak ret() i wywołał po prostu konstruktor w ciele naszego przykładu.
Storage ret() {
    Storage tmp("local_val");
    return tmp;
}

void returnValue1() {
    Storage st("val_1");
    st = ret();
}

void returnValue2() {
    Storage st = ret();
}

int main() {
    cout << "returnValue1():" << endl;
    returnValue1();
    cout << "returnValue2():" << endl;
    returnValue2();
}
Wyniki dla returnValue*:
returnValue1():
Constructor:      val_1
Constructor:      local_val
Move constructor: local_val
Destructor:       old local_val, data was moved from here
operator= move:   val_1 := local_val
Destructor:       old local_val, data was moved from here
Destructor:       old val_1, now assigned local_val

returnValue2():
Constructor:      local_val
Move constructor: local_val
Destructor:       old local_val, data was moved from here
Move constructor: local_val
Destructor:       old local_val, data was moved from here
Destructor:       local_val
Ostatni przykład pokazuje, że do funkcji można przekazać parametr korzystając z std::move, a więc niejako "prosząc" o skorzystanie z mechanizmu move semantic. Gdyby parametr był oznaczony modyfikatorem const, nie było by to możliwe.
Storage set(Storage st_set) {
    return st_set;
}

void settingValue1() {
    Storage st1("val_1");
    Storage st = set(st1);
}

void settingValue2() {
    Storage st = set(Storage("val_1"));
}

void settingValue3() {
    Storage st1("val_1");
    Storage st = set(std::move(st1));
}

int main() {
    cout << "settingValue1():" << endl;
    settingValue1();
    cout << "settingValue2():" << endl;
    settingValue2();
    cout << "settingValue3():" << endl;
    settingValue3();
}
Wyniki dla settingValue*.
settingValue1() - "copy elision" OFF:
Constructor:      val_1
Copy constructor: val_1
Move constructor: val_1
Move constructor: val_1
Destructor:       old val_1, data was moved from here
Destructor:       old val_1, data was moved from here
Destructor:       val_1
Destructor:       val_1

settingValue1() - "copy elision" ON:
Constructor:      val_1
Copy constructor: val_1
Move constructor: val_1
Destructor:       old val_1, data was moved from here
Destructor:       val_1
Destructor:       val_1

settingValue2():
Constructor:      val_1
Move constructor: val_1
Move constructor: val_1
Move constructor: val_1
Destructor:       old val_1, data was moved from here
Destructor:       old val_1, data was moved from here
Destructor:       old val_1, data was moved from here
Destructor:       val_1

settingValue3():
Constructor:      val_1
Move constructor: val_1
Move constructor: val_1
Move constructor: val_1
Destructor:       old val_1, data was moved from here
Destructor:       old val_1, data was moved from here
Destructor:       val_1
Destructor:       old val_1, data was moved from here
Do funkcji pass możemy przekazać tylko rvalue reference (przez rzutowanie std::move), wszelkie próby wsadzenia parametru lvalue ("jeśli zmienna ma nazwę to jest lvalue") zakończą się błędem. Taki parametr najczęściej pojawia się w metodach bibliotecznych i informuje, że możemy ukraść zasoby ze zmiennej. Towarzyszy jej bliźniacza metoda (przyjmująca const T&) będąca wersją służącą do kopiowania zasobów.
Storage pass(Storage&& st) {
    return st;
}

void passingValue1() {
    Storage st1("val_1");
    Storage st = pass(std::move(st1));
}

int main() {
    cout << "passingValue1():" << endl;
    passingValue1();
}
Wynik dla passingValue*:
passingValue1():
Constructor:      val_1
Copy constructor: val_1
Move constructor: val_1
Destructor:       old val_1, data was moved from here
Destructor:       val_1
Destructor:       val_1
Próba zdefiniowania własnego konstruktora kopiującego, operatora przypisania albo destruktora kończy się tym, że kompilator nie wygeneruje za nas operatora i konstruktora move.
struct StorageDestructor : public Storage {
    ~StorageDestructor() noexcept {
        cout << "Own destructor:   " << data << endl;
    }
};

void deriveWithDestructor1() {
    StorageDestructor st1;
    StorageDestructor st2;
    st1 = std::move(st2);
}

int main() {
    cout << "deriveWithDestructor1():" << endl;
    deriveWithDestructor1();
}
Wynik dla deriveWithDestructor1:
deriveWithDestructor1():
Default constr.:  defaultData
Default constr.:  defaultData
operator= copy:   defaultData := defaultData
Own destructor:   defaultData
Destructor:       defaultData
Own destructor:   old defaultData, now assigned defaultData
Destructor:       old defaultData, now assigned defaultData
Kolejny przykład. Choć klasa przez zdefiniowanie własnego destruktora zapobiega stworzeniu (implicit) konstruktora i operatora move, nadal mamy możliwość wymusić ich powstanie przez skorzystanie z default.
struct StorageWithDefaultMove : public Storage {
    // Destruktor zapobiega niejawnemu wygenerowaniu operatora move
    ~StorageWithDefaultMove() noexcept { }
    // Wymuszamy wygenerowania operatora move!
    StorageWithDefaultMove& operator=(StorageWithDefaultMove&&) = default;
};

void dervieWithOwnMoveAssign1() {
    StorageWithDefaultMove st1;
    StorageWithDefaultMove st2;
    st1 = std::move(st2);
}

int main() {
    cout << "dervieWithOwnMoveAssign1():" << endl;
    dervieWithOwnMoveAssign1();
}
Wynik dla dervieWithOwnMoveAssign1:
dervieWithOwnMoveAssign1():
Default constr.:  defaultData
Default constr.:  defaultData
operator= move:   defaultData := defaultData
Destructor:       old defaultData, data was moved from here
Destructor:       old defaultData, now assigned defaultData

Reference qualifier


Nowe definicje i mechanizmy wstecznej kompatybilności sprawiły, że biblioteka standardowa pozwala teraz na przypisywanie do rvalue! Czasami takie użycie może być zaskakujące. Poniższy kod jest prawidłowy.
string s1;
string s2;
s1 + s1 = "Hello world!";
Możemy się przed tym ustrzec w naszych klasach, zmuszając by operand lewostronny (obiekt, na który wskazuje this), był lvalue. Pozwala na to reference qualifier (& dla lvalue, oraz && dla rvlaue), który stosuje się tak jak const, gdy chcemy oznaczyć metody, które nie mogą modyfikować pól obiektu.

Tak jak const, kwalifikatory te można stosować dla funkcji nie statycznych, muszą też znajdować się w deklaracji i definicji, a jeżeli występują razem z const zapisuje się je na końcu. Jeżeli definiujemy dwie lub więcej metod, które mają takie same nazwy i listę parametrów, musimy wprowadzić reference qualifier dla nich wszystkich, albo dla żadnej.

Przykład. Utworzona został operator=, ale tylko dla lvalue. Dodatkowo stworzone zostały dwie metody info, wołające różny kod w zależności od tego na jakim obiekcie zostały zawołane.
#include <iostream>
using namespace std;

class Foo {
public:
    Foo operator+(const Foo &) {
        return *this;
    }
    Foo &operator=(const Foo &) & {
        return *this;
    }

    void info() const & {
        cout << "info: lvalue" << endl;
    }
    void info() && {
        cout << "info: rvalue" << endl;
    }
};

Foo retFoo() {
    return Foo();
}

int main() {
    Foo f1;
    Foo f2;
    Foo f3;

//  (f1 + f2) = f3;     // Error - zapis do rvalue, ale brak takiego operatora
//  retFoo() = f3;      // Error - zapis do rvalue, ale brak takiego operatora

    f1.info();
    retFoo().info();
}
Wynik:
info: lvalue
info: rvalue

14 lutego 2014

[C++11] rvalue reference

Przebrnięcie przez move semantic wymaga wcześniejszego zapoznania się z rvalue reference (definicja wprowadzona już w C++98). Temat sprawił mi nie lada kłopot i z pewnością do niego powrócę jak tylko będę miał lepsze zrozumienie. Na razie notatki w takiej postaci i mam nadzieje, że nie ma w nich zbyt wielu błędów. Zbiór linków:

rvalue reference


Najpierw definicja lvalue/rvalue, które znalazłem pod pierwszym linkiem:
lvalue jest wyrażeniem, które odnosi się do jakiegoś miejsca w pamięci i możemy uzyskać adres tego miejsca korzystając z operatora &. rvalue jest wyrażeniem, które nie jest lvalue.
Można też stosować definicję "if it has a name ..." - link.
Jeżeli zmienna posiada nazwę to możemy uzyskać adres tej zmiennej, dzięki operatorowi &, wtedy wiem, że jest to lvalue. Nie możemy np. uzyskać nazwy literału (liczba 42), więc wiemy, że jest to rvalue.
lvalue reference mają trwały stan, tymczasem rvalue reference są albo literałami, albo tymczasowymi obiektami utworzonymi w czasie ewaluowania wyrażenia. Mają zatem ważne właściwości [C++Primer - s.533]:
  • Odnoszą się tylko do obiektów, które zostaną zaraz zniszczone
  • Nie ma innych użytkowników danego obiektu
Możemy więc bezpiecznie ukraść zasoby, do których odnosi się rvalue reference. Poniżej kilka operacji z wykorzystaniem tego mechanizmu.
#include <iostream>

using namespace std;

int main() {
    int i = 42;         // zmienna i jest lvalue

    int& lv1 = i;       // lvalue reference - referencja na i
//  int& lv2 = i * 5;   // i*5 to rvalue (stała) nie możemy zrobić referencji która pozwala na jej modyfikację

    const int& lv3 = i * 5;  // lvalue reference - możemy, ustawić referencję na stałą

//  int&&  rv1 = i;     // rvalue reference - nie może pokazywać na lvalue
    auto&& lv4 = i;     // lvalue reference - następuje dedukcja typu na "int&"

    int&& rv2 = 42;     // rvalue reference - 42 to literał
    int&& rv3 = i * 3;  // rvalue reference - wynik mnożenia to ulotna wartość (rvalue)

//  int&& rv4 = rv2;    // rv2 nie jest ulotne i jest ważne do końca zakresu!
    int&& rv5 = std::move(rv2); // rvalue reference - obietnica, że nie będziemy korzystać z rv2!
}

Reference collapsing i dedukcja typu


Logika podpowiada, że wszystko co ma postać T&& można by nazwać rvalue reference. Niestety nie jest to prawda, zależy to bowiem od kontekstu w jakim ta postać istnieje. Zgodnie z nowym standardem wszędzie tam, gdzie mamy do czynienia z dedukcją typu (szablony, auto, typedef oraz pewne konstrukcje decltype) i jest symbol &&, czasami możemy mieć rvalue reference, a czasami lvalue reference. Scott Meyers, proponuje swoją terminologię i nazywanie takich referencji universal reference, bowiem taka referencja może pokazywać na "wszystko": lvalue, rvalue, const, non-const, ...

Kiedy parametr funkcji jest typu T&&, a dodatkowo mamy do czynienia z dedukcją typu (bo funkcja jest szablonowa), to typ może ulec zmianie. Standard przewiduje trzy wyjątki (w książce dwa).
  • Dla argumentu będącego lvalue => T będzie lvalue referencją (czyli T&)
  • Dla argumentu będącego rvalue => T będzie po prostu T - o tej zasadzie wspomina Meyers w swoim wykładzie [strona 26], nie znalazłem tego w książce.
  • Jeśli stworzymy referencję do referencji (choć nie bezpośrednio) wtedy ta referencja się "zapada". rvalue reference zapada się do rvalue reference, w pozostałych przypadkach zapadanie jest do lvalue reference.
    • T& & => T&
    • T& && => T&
    • T&& & => T&
    • T&& && => T&&
Widać te zasady na przykładach poniżej, gdy np. dla zmiennej int v1, wydedukowany zostanie typ int&. Brakuje mi tylko przykładu kiedy T&& && zapada się do T&&. Może uda mi się kiedyś coś znaleźć.

Przykłady:
#include <iostream>

// T&& to universal reference
template <typename T>
void f(T&& t, std::string name, std::string descr) {
    std::cout << descr << std::endl;
    if (std::is_const<typename std::remove_reference<decltype(t)>::type>::value)
        std::cout << name << " is const" << std::endl;
    if (std::is_lvalue_reference<decltype(t)>::value)
        std::cout << name << " is lvalue reference" << std::endl;
    if (std::is_rvalue_reference<decltype(t)>::value)
        std::cout << name << " is rvalue reference" << std::endl;
    std::cout << std::endl;
}

int main() {
    f(42, "42", "f<int>(int &&) => f<int>(int&&)");

    int v1 = 42;          // lvalue
    f(v1, "v1", "f<int&>(int& &&) => f<int&>(int&)");

    const int v2 = 42;    // const lvalue
    f(v2, "v2", "f<const int&>(const int& &&) => f<const int&>(const int&)");

    int tmp = 42;         // lvalue
    int& v3 = tmp;        // lvalue reference
    f(v3, "v3", "f<int&>(int& &&) => f<int&>(int&)");

    const int& v4 = 42;   // lvalue reference na const
    f(v4, "v4", "f<const int&>(const in& &&) => f<const int&>(const int&)");

    int&& v5 = 42;        // rvalue reference
    if (std::is_rvalue_reference<decltype(v5)>::value)
        std::cout << "in main v5 if rvalue reference" << std::endl;
    f(v5, "v5", "f<int&>(int& &&) => f<int&>(int&)");

    const int&& v6 = 42;  // rvalue reference na const
    if (std::is_rvalue_reference<decltype(v6)>::value)
        std::cout << "in main v6 if rvalue reference" << std::endl;
    f(v6, "v6", "f<const int&>(const int& &&) => f<const int&>(const int&)");

    int v7 = 67;
    f(std::move(v7), "v7", "f<int>(int &&) => f<int>(int&&)");
}
Chociaż v5 i v6 to rvalue referencje, dzięki nazwie możemy uzyskać ich adres, więc w środku funkcji f(), będą to już lvalue referencje. Aby przekazać rvalue referencję do środka, trzeba skorzystać z std::move. Wynik:
f<int>(int &&) => f<int>(int&&)
42 is rvalue reference

f<int&>(int& &&) => f<int&>(int&)
v1 is lvalue reference

f<const int&>(const int& &&) => f<const int&>(const int&)
v2 is const
v2 is lvalue reference

f<int&>(int& &&) => f<int&>(int&)
v3 is lvalue reference

f<const int&>(const in& &&) => f<const int&>(const int&)
v4 is const
v4 is lvalue reference

in main v5 if rvalue reference
f<int&>(int& &&) => f<int&>(int&)
v5 is lvalue reference

in main v6 if rvalue reference
f<const int&>(const int& &&) => f<const int&>(const int&)
v6 is const
v6 is lvalue reference

f<int>(int &&) => f<int>(int&&)
v7 is rvalue reference
Z czterech form, dla których może następować dedukcja typu, trzy zachowują się tak samo: typ szablonowy, auto oraz typedef. Niestety decltype, posiada pewne wyjątki od tej reguły, ale w tej chwili, nie chce mi się w to zagłębiać. Może kiedyś.

Value categories


W C++11 wraz z wprowadzeniem move semantic, liczba kategorii została rozbudowana:
Zależności przedstawia poniższe drzewo:
@startuml "value_categories.png"

(expression) --> (glvalue)
(glvalue) --> (lvalue)
(glvalue) --> (xvalue)

(expression) --> (rvalue)
(rvalue) --> (xvalue)
(rvalue) --> (prvalue)

@enduml

Ich właściwości można podsumować w ten sposób:
  • glvalue ("generalized" lvalue)
    • mają nazwę
  • rvalue (nazwa historyczna, bo mogły się pojawić po prawej stronie operatora przypisania)
    • można z nich przenosić
  • lvalue (nazywa historyczna, bo mogły się pojawić po lewej stronie operatora przypisania)
    • mają nazwę i można z nich przenosić
  • xvalue ("eXpiring" value)
    • mają nazwę i można z nich przenosić
  • prvalue (pure rvalue)
    • nie mają nazwy i można z nich przenosić
O ile prvalue najbardziej mi przypomina starą definicję rvalue (nie ma nazwy), to teraz w jej skład (patrz drzwo) wchodzi również xvalue. Jest kilka przykładów tego wyrażenia:
  • wywołanie funkcji, której wynikiem jest rvalue reference (np. std::move(x) zwraca rvalue reference do x)
  • a[n], wyrażenie, gdzie a jest tablicą rvalue
  • a.m, wyrażenie, gdzie a jest rvalue i m jest składową non-static typu non-reference
  • wyrażenie rzutowania do rvalue reference (np. static_cast<char&&>(x))

3 lutego 2014

Code reading

Fajny artykuł dotyczący czytania kodu, stojący trochę w opozycji do tego, że kod powinno się czytać jak dobrą książkę. I trochę szczery, deweloperzy nie mają jednak nawyku czytania kodu...

26 stycznia 2014

[BlackHat] Karsten Nohl - Rooting Sim Cards (USA 2013)

Kolejny bardzo udany wykład, Karsten Nohl na temat przełamywania zabezpieczeń w sieciach telekomunikacyjnych. Tym razem na temat rootowania kard SIM:
Operator ma możliwość instalowania na naszej karcie SIM, swojego oprogramowania np. preferowanych operatorów wykorzystywanych podczas roamingu. Instalacja odbywa się przez wysłanie binarnej wiadomości SMS (specjalny rodzaj - identyfikowany przez odpowiedni nagłówek), z komendą/aplikacją podpisaną (DES/3DES/AES) przez operatora. Taki sam klucz (wgrany kiedyś przez operatora na kartę) używany jest do stworzenia podpisanej odpowiedzi. Odpowiedzią, może być error informujący o źle podpisanej pierwotnej wiadomości. Odpowiedź (i czy w ogóle jest) zależy od telefonu i wynika najpewniej z różnej interpretacji standardu. Nohl-owi udało się złamać taki klucz (DES), tworząc tęczową tablicę.
Wgranie własnej aplikacji, niesie ciekawe perspektywy, np. pełne sklonowanie karty. Jak twierdzi Nohl, można ominąć sandbox - przynajmniej w dwóch popularnych implementacjach Java Card VM. Podatności jest ponoć dużo więcej i są dość proste do znalezienia, ale szczegóły zachował dla siebie.

25 stycznia 2014

[CERN] ROOT library - instalacja

Pierwsze podejście do biblioteki ROOT tworzonej przez CERN, z myślą o fizykach. Opowiadał o niej Vassil Vassilev na konferencji C++Now, kiedyś już podsyłałem linka: Interactive, Introspected C++ at CERN. Niestety nie znalazłem paczki, z której mógłbym sobie to zainstalować więc pozostała metoda ręcznej kompilacji. ROOT oferuje kilka ścieżek, na razie wybrałem najbardziej leniwą. Celem, jest odpalenie najprostszego przykładu. Linki:
Konfiguracja wymogła na mnie doinstalowania dwóch paczek: libxpm-dev oraz libxft-dev. Dla około 400 MB kodu, kompilacja trwała jakieś 3.5 godziny. Budowanie:
$ git clone http://root.cern.ch/git/root.git
$ cd root
$ ./configure
$ make

   ============================================================
   ===                ROOT BUILD SUCCESSFUL.                ===
   === Run 'source bin/thisroot.[c]sh' before starting ROOT ===
   ============================================================

# Następnie trzeba wykonać skrypt, który ustawi ścieżki do 
# plików wykonywalnych (PATH) i biblioteki (LD_LIBRARY_PATH)
$ source ./bin/thisroot.sh
Teraz zostajemy zachęceni do wykonania komendy "root", która uruchamia interpreter cling-a.
cling C/C++ Interpreter: type .? for help.
root [0] printf("Hello World!\n");
Hello World!
root [1] 

18 stycznia 2014

[python] doctest

doctest jest modułem dodanym do standardowej biblioteki Python-a, umożliwiający testowanie wyjścia z interpretera. Testy ujęte są w docstring, wejście (action) rozpoznawane jest po >>>, tak jak to wygląda w interpreterze, następna linia jest oczekiwanym wynikiem. Domyślnie, dostest informuje jedynie o porażkach (można to jednak zmodyfikować).
import doctest

def fun():
    '''
    # Testing sorted()
    >>> sorted([1, 3, 2])
    [1, 2, 3]

    # Testing max()
    >>> max(1, 2)
    1
    '''
    pass

doctest.testmod()
Wynik:
Failed example:
    max(1, 2)
Expected:
    1
Got:
    2
**********************************************************************
1 items had failures:
   1 of   2 in __main__.fun
***Test Failed*** 1 failures.

11 stycznia 2014

[python] Wykrajanie sekwencji

Wykrajanie (slice) sekwencji:
#
# s[i:j:k] -> wycinek s od i do j - [i, j) - z krokiem k
#
s = [0, 1, 2, 3, 4]

# stwórz sekwencje z elementów o indeksach [i, j), i <= j
print s[1:3]
print s[1:1] # [i, i) -> zbiór pusty
[1, 2]
[]

# jeżeli i jest pominięte lub równe None, przyjmij że jest równe 0
print s[:2] # s[0:2]
[0, 1]

# jeżeli j jest pominięte lub równe None, przyjmij że jest równe len(s)
print s[2:]
[2, 3, 4]

# jeżeli i lub j jest większe od len(s), użyj len(s)
print s[2:800]
[2, 3, 4]

# dla i, j negatywnych, oblicz nowe i := len(s) + i albo j := len(s) + j
print s[-1]
print s[-1:1] # odpowiednik s[4, 1], ponieważ i > j dostaniemy zbiór pusty
4
[]

# wybierz co k-ty element (i, i+k, i+2*k, ...) z sekwencji pomiędzy indeksami [i, j)
print s[0:4:2]
print s[::2]   # s[0:len(s):2] -> s[0:4:2]
[0, 2]
[0, 2, 4]

# jeżeli i lub j są pominięte stają się wartościami końcowymi ("end"), to jaka to
# wartość końcowa, zależy od znaku k
# dla k < 0, sekwencja będzie odwrócona, czyli s' := [4, 3, 2, 1, 0]
print s[::-1]  # odpowiednik -> s'[0:len(s):-1]
print s[::-2]  # odpowiednik -> s'[0:len(s):-1]
print s[:2:-1] # odpowiednik -> s'[0:2:-1]
[4, 3, 2, 1, 0]
[4, 2, 0]
[4, 3]

# k nie może być równe 0
print s[1:4:0] # Wyjątek!
ValueError: slice step cannot be zero