29 grudnia 2012

include-what-you-use, pierwsze podejście

Na GoingNative 2012, Chandler Carruth wspominał o narzędziu (korzystającego z API clang-a), które ułatwiało by zarządzanie plikami nagłówkowymi, sugerując przeniesienie ich deklaracja do miejsc, gdzie są naprawdę potrzebne, co istotnie miało przyśpieszyć czas kompilacji. Nie udało mi się uzyskać informacji, gdzie to narzędzie można znaleźć. Pocztą pantoflową, dowiedziałem się o include-what-you-use.

Dodatkowe, przydatne linki:
http://clang.llvm.org/get_started.html
http://code.google.com/p/include-what-you-use/wiki/InstructionsForUsers

Niestety pierwsza, próba zakończyła się dla mnie porażką. Korzystając z wersji "trunk" obu projektów, kompilacja nie powiodła się. Postanowiłem więc skorzystać z odrobinę starszych wersji (RELEASE_30/final), dla obu projektów. Wydawało mi się, że tak zsynchronizowane projekty, już razem zadziałają. Miałem rację, przynajmniej jeśli chodzi o kompilację. Poniżej, sekwencja poleceń, która mi to zapewniła:
svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_30/final llvm

cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_30/final clang
cd ../..

cd llvm/projects
svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_30/final/projects compiler-rt
cd ../..

cd llvm/tools/clang/tools
svn co http://include-what-you-use.googlecode.com/svn/tags/clang_3.0/ include-what-you-use
cd ..
# Wyedytować tools/clang/tools/Makefile i dodać include-what-you-use do zmiennej DIRS
# Wyedytować tools/clang/tools/CMakeLists.txt i dodać add_subdirectory(include-what-you-use)
cd ../../..

# Aby nie zaśmiecać, tworzymy katalog specjalnie na build (nie będzie make install)
mkdir build
cd build
../llvm/configure
make
Niestety, dla mojego testowego projektu, działanie programu kończy się błędem (co jest oczekiwane - nie wiem, tylko czy to ma być taki błąd), i nie generuje raportu.
make -k CXX=~/build/Debug/bin/include-what-you-use > log
Ku pamięci, mam nadzieje, że wrócę jeszcze kiedyś do tematu.

28 grudnia 2012

anonymous/unnamed namespace

Nienazwane namespace, mają zastąpić static globals. Ich głównym zadaniem jest ukrycie czegoś. Można się do nich dostać tylko w danej jednostce kompilacji, którą jest plik cpp. Każdy nienazwany namespace jest unikalny. Jednym z przykładów, może być chęć stworzenia “lokalnej” klasy w pliku cpp, ale ponieważ klasa o takiej nazwie już istnieje w projekcie, wystąpi błąd. Rozwiązaniem jest skorzystanie z tego mechanizmu.

Plik Second.hpp:
#ifndef SECOND_HPP
#define SECOND_HPP
#include <iostream>

using namespace std;

namespace bbb
{
    namespace
    {
        int b = 77;
    }

    class Klasa {
    public:
        void count2();
    };
}
#endif // SECOND_HPP
Plik Second.cpp:
#include <vector>
#include <boost/assign/list_of.hpp>
#include "Second.hpp"

namespace bbb
{
    namespace
    {
        int value = 2;

        bool isEven(int i) {
            return (i == value);
        }
    }

    void Klasa::count2() {
        vector<int> v = boost::assign::list_of(1)(2)(3)(2)(1)(1);
        cout << "call bbb::Klasa::count2() = " << count_if(begin(v), end(v), isEven) << endl;
    }
}
Plik main.cpp:
#include <iostream>
#include "Second.hpp"

using namespace std;

namespace std
{
    void moja() {
        cout << "call std::moja()" << endl;
    }
}

namespace aaa
{
    namespace
    {
        std::string a = "asdf";
        void access() {
            cout << "call aaa::anonymous::access()" << endl;
        }
    }

    void show() {
        cout << "call aaa::show()" << endl;
        access();
    }
}

int main()
{
    std::moja();
    aaa::access();
    aaa::show();

    bbb::Klasa s;
    s.count2();

//  bool l = bbb::isEven(4);    // ERROR!

    cout << "Access to member aaa::a = " << aaa::a << endl;
    cout << "Access to member bbb::b = " << bbb::b << endl;
//  cout << "Access to member bbb::value = " << bbb::value << endl;    // ERROR!

    return 0;
}
Wyniki:
call std::moja()
call aaa::anonymous::access()
call aaa::show()
call aaa::anonymous::access()
call bbb::Klasa::count2() = 2
Access to member aaa::a = asdf
Access to member bbb::b = 77

27 grudnia 2012

List Comprehensions

Nigdy, tego do końca nie rozumiałem, więc przyszła pora, żeby się w to zagłębić. Budowa tego cuda wygląda następująco:
result = [*transform* *iteration* *filter*]
Pierwsze wykonywana jest iteracja (jakaś pętla), jej wyniki podlegają filtrowaniu, to co przejdzie filtrowanie (opcjonalny mechanizm), może zostać jeszcze przetransformowane, a ostateczny wynik trafia jako element listy result.
Prosty przykład:
print [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Do filtra trafiają wyniki iteracji, jest to konstrukcja "if", jeżeli zwróci True, wartość zostanie przekazana do transformowania. W przykładzie powyżej transformacji nie ma, wynik jest umieszczany jako element listy. Tutaj przykład z użyciem filtra.
def ftr(x):
    if x % 2 == 0:
        return True
    return None

print [x for x in range(10) if ftr(x)]
[0, 2, 4, 6, 8]
Wydaje mi się (nie udało mi znaleźć), że do konstrukcji filtra nie można używać "else" albo "elif", ale zastosowanie warunków logicznych wydaje się wystarczające.
print ['zero' if x == 0 else 'six' if x == 6 else str(x) + '?'
       for x in range(10) if x % 2 == 0 or x % 3 == 0]
['zero', '2?', '3?', '4?', 'six', '8?', '9?']
W przypadku transformacji, możemy skorzystać z instrukcji warunkowej "else". Nie można skorzystać z "elif", ale bez tego można sobie poradzić. Poniżej ekwiwalent transformacji, jaka została użyta, w konstrukcji listy.
def tsfm(x):
    if x == 0:
        return 'zero'
    else:
        if x == 6:
            return 'six'
        else:
            return str(x) + '?'

26 grudnia 2012

C++11/17 - inicjalizacja zmiennych

Nowy standard dostarcza nowy sposób inicjalizacji zmiennych (można nareszcie wygodnie inicjalizować kontenery). Co ciekawe, w przypadku typów prostych i konwersji, która może doprowadzić do utraty danych kompilator wyświetli ostrzeżenie.
#include <iostream>

using namespace std;

int main()
{
    long double ld = 3.1415926536;
    int curly_from_ld_1{ld};    // warning and data lost
    int curly_from_ld_2 = {ld}; // warning and data lost
    cout << ld << " " << curly_from_ld_1 << " " << curly_from_ld_2 << endl;

    int round_from_ld_1(ld);    // no-warning, data lost
    int round_from_ld_2 = ld;   // no-warning, data lost
    cout << ld << " " << round_from_ld_1 << " " << round_from_ld_2 << endl;

    long long ll = 5000000000;
    int curly_from_ll_1{ll};    // warning and data lost
    int curly_from_ll_2 = {ll}; // warning and data lost
    cout << ll << " " << curly_from_ll_1 << " " << curly_from_ll_2 << endl;

    int i = 56;
    long long curly_from_int{i};
    cout << curly_from_int << endl;

    return 0;
}

Testowane z g++ (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008,
$ g++ -std=c++17 main.cpp 

main.cpp: In function ‘int main()’:
main.cpp:8:25: warning: narrowing conversion of ‘ld’ from ‘long double’ to ‘int’ [-Wnarrowing]
    8 |     int curly_from_ld_1{ld};    // warning and data lost
      |                         ^~
main.cpp:9:28: warning: narrowing conversion of ‘ld’ from ‘long double’ to ‘int’ [-Wnarrowing]
    9 |     int curly_from_ld_2 = {ld}; // warning and data lost
      |                            ^~
main.cpp:17:25: warning: narrowing conversion of ‘ll’ from ‘long long int’ to ‘int’ [-Wnarrowing]
   17 |     int curly_from_ll_1{ll};    // warning and data lost
      |                         ^~
main.cpp:18:28: warning: narrowing conversion of ‘ll’ from ‘long long int’ to ‘int’ [-Wnarrowing]
   18 |     int curly_from_ll_2 = {ll}; // warning and data lost
      |                            
Wynik:
3.14159 3 3
3.14159 3 3
5000000000 705032704 705032704
56

Uniform initialization

W C++17 prawie się udało wprowadzić ujednolicony sposób inicjalizacji. Ciągle jeszcze są pewne problemy (linijka 14, a także coś z atomics i agregatami), ale zdaje się że zostaną one naprawione w przyszłych wersjach standardu. W każdym razie programiści są zachęcani, by wszędzie tam gdzie to możliwe stosować klamerki w celu inicjalizacji [1].
#include <iostream>
#include <boost/type_index.hpp>

using namespace std;

int main()
{
    auto var1{17};          // std::initializer_list<int> w C++11, w C++17 int
//  auto var2{1, 3, 6};     // std::initializer_list<int> w C++11, w C++17 ERROR

    cout << boost::typeindex::type_id_with_cvr<decltype(var1)>().pretty_name() << endl;
//  cout << boost::typeindex::type_id_with_cvr<decltype(var2)>().pretty_name();

    auto var3 = {1, 3, 6};  // Ups nieintuicyjne, nadal std::initializer_list<int>

    cout << boost::typeindex::type_id_with_cvr<decltype(var3)>().pretty_name() << endl;
}
Wynik:
int
std::initializer_list<int>
Inne ciekawostki, to inicjalizacja FDT (fundamental data types) domyślną wartością (0, false, nullptr), gdy nawiasy klamerkowe będą puste. Struktury (także dziedziczące), możemy inicjalizować za pomocą zagnieżdżonych nawiasów, ale dodano także wygodne odwoływanie się do nazw pól:
#include <iostream>

using namespace std;

struct Base {
    int age;
    double high;
};

struct Child : public Base {
    string fun() { return name; };
    string name;
};

int main()
{
    int val{};
    cout << "FDT (fundamental data type): " << val << endl;

    Child child1{};     // czyli to samo co child1{0, 0, ""};
    cout << child1.age << " " << child1.high << " " << child1.name << endl;

    Child child2{{.age=1, 2}, .name="Tom"};
    cout << child2.age << " " << child2.high << " " << child2.name << endl;
}
Wynik:
FDT (fundamental data type): 0
0 0 
1 2 Tom

23 grudnia 2012

Prosty Makefile

Musiałem zbudować, bardzo mały plik Makefile. Tutaj przydatny tutorial:
http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/

Make bez argumentów uruchomi, pierwszą napotkaną regułę, w tym przypadku będzie to main. Po dwukropku możemy podać listę (oddzieloną spacjami) zależność np. nazwy reguł na zbudowanie plików *.o, zanim, będziemy z nich korzystać podczas linkowania. Komendy, które składają się na daną regułę muszą być poprzedzone tabulatorem!
CXX=g++

main:
        $(CXX) -o main main.cc Lion.cc Lungs.cc -I.

Istnieje możliwość przekazania parametrów, do skryptu, w poniższym przykładzie, zmieniamy kompilator z domyślnego gcc, na clang-a.
$ make CXX=clang++
clang++ -o main main.cc Lion.cc Lungs.cc -I.

25 listopada 2012

Benchmark framework - zużycie pamięci - valgrind Massif

Moje poszukiwania, mechanizmu pozwalającego zdobyć informację na temat maksymalnego zużycia pamięci działającego programu zaowocowały spotkaniem z tym oto narzędziem: http://valgrind.org/docs/manual/ms-manual.html. Z pewnością są lepsze rozwiązania, ale dla moich potrzeb to jest satysfakcjonujące, przynajmniej w tej chwili.
Zaczniemy do przykładu, którym ma zając się valgrind.
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
T * alloc()
{
    cout << "size " << sizeof(T) << endl;
    return new T[1000000];
}

void alloc_dealloc()
{
    int * a = alloc<int>();
    delete [] a;
}

int main()
{
    alloc_dealloc();
    vector<char> v(100000);
    alloc<long long>();
    alloc_dealloc();
    for (int i = 0; i < 300; ++i)
        alloc_dealloc();

    return 0;
}
Taki projekt trzeba skompilować, generując informacja dla debuggera.
$ g++ -g main.cpp
Teraz nasz program można poddać analizie
$ valgrind --tool=massif --time-unit=B --stacks=yes --massif-out-file=mem.out ./a.out
$ ms_print mem.out
* pierwsza linijka włącza massif do analizy (--tool=massif),
* "jednostka czasu" używana przez profiler - nie jestem pewny jak to rozumieć.
The time unit used for the profiling. There are three possibilities: instructions executed (i), which is good for most cases; real (wallclock) time (ms, i.e. milliseconds), which is sometimes useful; and bytes allocated/deallocated on the heap and/or stack (B), which is useful for very short-run programs, and for testing purposes, because it is the most reproducible across different machines.
W każdym razie, jest zalecana do małych programów i w celach testowych, więc to czego szukam.
* informuje, że interesuje nas też pamięć, która zostanie zaalokowana na stosie (--stack=yes),
* jak będzie nazywał się surowy plik z analizą zużycia pamięci (--massif-out-file=mem.out),

Massif rozdziela proces zbierania danych od ich prezentacji, w ten sposób w przyszłości mogą pojawić się nowe metody na ich prezentowanie. Plik, gdzie zostały zgromadzone dane poddajemy działaniu ms_print.
Number of snapshots: 72
 Detailed snapshots: [1 (peak), 8, 27, 32, 37, 42, 47, 55, 65]

--------------------------------------------------------------------------------
  n        time(B)         total(B)   useful-heap(B) extra-heap(B)    stacks(B)
--------------------------------------------------------------------------------
  0              0                0                0             0            0
  1     21,983,976       12,103,920       12,100,000         3,576          344
Mnie interesują najbardziej dwie linijki z całego raportu. Pierwsza to numer snapshota, który zanotował największy "peak" (1). A druga do dokładne informacje z tego snapshota, czyli całkowite zużycie pamięci, w tym przypadku 12103920 bajtów.

23 listopada 2012

Boost.Bimap

Szybkie wprowadzanie do bimapy: http://www.boost.org/doc/libs/1_42_0/libs/bimap/doc/html/boost_bimap/one_minute_tutorial.html
Bimap-a mapuje jeden klucz na drugi. W mapach klucze muszą być niepowtarzalne, bimapach każdy występujący w nich klucz i wartość musi być niepowtarzalny (wartość też jest kluczem).
#include <iostream>
#include <boost/bimap.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>

using namespace std;

template <typename T>
void show(T m)
{
    for (typename T::iterator it = m.begin(); it != m.end(); ++it)
        cout << "BiMap[" << it->first << "] = " << it->second << endl;
}

int main()
{
    typedef boost::bimap<int, string> BMap;
    BMap bm = boost::assign::list_of<BMap::relation>(1, "aaa")
                                                    (1, "zzz")
                                                    (2, "bbb")
                                                    (3, "ccc")
                                                    (4, "aaa");

    show(bm.left);
    show(bm.right);

    return 0;
}
W przykładzie nie możemy do bimapy wprowadzić nowych kluczy bo już wcześniej zastały wstawione.
Linijka 19 - istnieje już klucz 1 (linijka 18)
Linijka 22 - istnieje już klucz "aaa" (linijka 18)
Aby wyszukać odpowiednią wartość, musimy wiedzieć co w danej chwili jest kluczem. W tym celu należy skorzystać z obiektów "left" (klucz1 -> klucz2) lub "right" (klucz2 -> klucz1).
Wyniki:
BiMap[1] = aaa
BiMap[2] = bbb
BiMap[3] = ccc
BiMap[aaa] = 1
BiMap[bbb] = 2
BiMap[ccc] = 3

7 listopada 2012

Struct - inicjalizacja zerami i -Wextra (część II)

A co gdyby, do inicjalizacji użyć pustych klamerek? Na stronie http://www.ex-parrot.com/~chris/random/initialise.html można przeczytać:
It would be neater to be able to say struct foo bar = {}, and some compilers (e.g. GCC) permit this, but the ANSI grammar requires an initialiser list to be non-empty.
Czyli generalnie nie można, bo niezgodne ze standardem. Trzeba więc przekonać się na własnej skórze.
struct Stru2 {
    int c;
    int d;
};

struct Stru1 {
    int a;
    Stru2 b;
};

int main()
{
    struct Stru1 aaa = {};
    return aaa.b.c;
}
Mimo, że GCC nam na to pozwala, to chciałem wymusić zgodność ze standardem ANSI, (chociaż bardziej mnie interesuje co się stanie w C++, a nie w C).
gcc -Wall -pedantic -ansi main.cpp
Hmm, obyło się bez protestów. Jeszcze jedna próba tym razem z -Wextra
gcc -Wall -Wextra -pedantic -ansi main.cpp
Tutaj się już coś dzieje. Ciekawe sprawa, bo nawet gdy skorzystam z zapisu "Stru1 aaa = {0}", to dalej są protesty o nie zainicjowane zmienne.
main.cpp:36:25: warning: missing initializer for member ‘Stru1::a’ [-Wmissing-field-initializers]
main.cpp:36:25: warning: missing initializer for member ‘Stru1::b’ [-Wmissing-field-initializers]
Zasięgnąłem opinii na stackoverflow.com i z odpowiedzi wynika, że -Wextra, martwi się bardziej niż pozawala na to standard i choć wszystko zostanie zainicjalizowane zerami, to teraz kompilator chciałby, żebyśmy sami zainicjalizowali każde pole z osobna. Jeszcze rzut oka, na to co wygenerował kompilator. Wszystko na zero ;)
.LFB0:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $16, %esp
    movl    $0, -12(%ebp)
    movl    $0, -8(%ebp)
    movl    $0, -4(%ebp)
    movl    -8(%ebp), %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc

4 listopada 2012

[C++] "Variadic template" oraz "fold expression"

Variadic template

W C++11 do szablonów można przekazywać nie tylko typy ale też wartości (non-type template parameter). Przykład poniżej pokazuje obliczanie średniej w tych dwóch wersjach. Dodatkowo pojawia się nowy sizeof...(), który potrafi powiedzieć z iloma parametrami mamy do czynienia.
#include <iostream>

using namespace std;


template <typename T>
int add(T last) {
    return last;
}

template <typename T, typename... Nums>
int add(T first, Nums... nums) {
    return first + add(nums...);
}

// non-type template parameter
template<int first, int... Nums>
double average1() {
    return (static_cast<double>(first) + add(Nums...)) / (sizeof...(Nums) + 1);
}

// type template parameter
template <typename T, typename... Args>
double average2(T first, Args... args) {
    return (static_cast<double>(first) + add(args...)) / (sizeof...(Args) + 1);
}

int main()
{
    cout << average1<1, 2, 3, 4>() << endl;
    cout << average2(1, 2, 3, 4) << endl;
}
Wynik:
2.5
2.5

Fold expression

W C++17 wprowadzono fold expression, które pozwala na stosowanie operatorów dla pakietu parametrów. W przykładzie ze średnią, nie trzeba więc stosować rekurencji. Trochę dziwne jest to że kompilator wymaga dwóch nawiasów, jeżeli chcemy rzutować wynik sumowania do double.
#include <iostream>

using namespace std;


template <typename... Args>
double average(Args... args) {
    return static_cast<double>((args + ...)) / sizeof...(Args);
}

int main()
{
    cout << average(1, 2, 3, 4) << endl;
}
Wynik:
2.5

Zadanie domowe

Na GoingNative 2012 Andrei Alexandrescu, przedstawił swój wykład "Variadic Templates are Funadic". Andrei bardzo lubi templejty, co widać ;). Variadic template mają w zamyśle być następcą/lepszą wersją wielokropka ("...") obecnego od czasów C, korzysta z niego np. printf(), z którego każdy lubi korzystać. Do lepszego zrozumienia mechanizmu rozwijania variadic template, autor zaproponował zadanie domowe - produkt kartezjański. Ale, żeby się do niego zabrać, trzeba było zrozumieć wykład (sic!), z pomocą przyszedł poniższy link, gdzie, ktoś rozwinął w kod, to co przedstawił Andrei na slajdach:

Wpierw, trzeba zmusić kompilator/CMake (gcc 4.6.3) do pracy z nowym C++11, więc poniżej modyfikacja CMakeList.txt
project(variadic_cpp11)
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++0x")
A teraz moje rozwiązanie - tworzą się pary wartości trzech typów i działa tylko dla wersji "A x A". W linijce 25 rozwinięcie, które było potrzebne, aby to osiągnąć. Aby można było przemnożyć między sobą dwa zbiory, trzeba by chyba skorzystać z nowego std::tuple. Ale w to się jeszcze nie wgłębiałem.
#include <iostream>
using namespace std;

template<class T>
int cartesianMult(T t)
{
    return 0;
}

template<class T, class U, class... Us>
int cartesianMult(T t, U u, Us... us)
{
    cout << endl << " (" << t << ", " << u << "),";
    cartesianMult<T, Us...>(t, us...);
    return 0;
}

template <class... T>
void fun(T...) {}

template <class... Ts>
void cartesianProduct(Ts... ts)
{
    cout << "{";
    fun( cartesianMult(ts, ts...)... );
    cout << endl << "}" << endl;
}

int main()
{
    cartesianProduct(1, 3.14, "abc");
    return 0;
}
A oto wyniki i ciekawostka. Typy, a więc i wartości brane są w odwrotnej kolejności, niż były wkładane do funkcji.
{
 (abc, 1),
 (abc, 3.14),
 (abc, abc),
 (3.14, 1),
 (3.14, 3.14),
 (3.14, abc),
 (1, 1),
 (1, 3.14),
 (1, abc),
}

1 listopada 2012

Kompilacja kodu assemblera i łączenie z g++

Jak zmusić kompilator do wygenerowania kodu assemblera, by go zmodyfikować i ponownie użycia do kompilacji?

Magiczny ciąg komend:
Po modyfikacji kodu assemblera, generujemy object-file, z którego ponownie skorzysta g++ (linijka 2).
g++ -S main.cpp 
as main.s -o main.o
g++ main.o -o main
Nasz kod, który chcemy zobaczyć w assemblerze:
#include <stdio.h>
int main()
{
    printf("Hello world\n");
    return 0;
}
Nasza modyfikacja w kodzie assemblerze (main.s):
.file    "main.cpp"
    .section    .rodata
.LC0:
    .string    "Hello Kitty ;)"
    .text
    .globl    main
    .type    main, @function
main:
.LFB0:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
    movl    $.LC0, (%esp)
    call    puts
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size    main, .-main
    .ident    "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits
Wynik:
Hello Kitty ;)

31 października 2012

Vector vs. List

Na tegorocznej konferencji GovingNative 2012 Bjarne Stroustrup, przedstawił kilka ciekawostek dotyczących nowości w C++:
Jednym z punków była wyższość wektora na listą. Lista góruje nad wektorem, w takich operacjach jak wstawianie oraz usuwanie elementów, jednak proces wyszukania odpowiedniego elementu w kontenerze okazuje się być naprawdę wąskim gardłem. Wektor z kolei lepiej radzi sobie z wyszukiwaniem, gorzej natomiast z usuwaniem i wstawianiem. Dzięki współczesnym procesorem korzystającym z mechanizmu dedukcji skoku, wektor okazuje się zyskiwać ogromną przewagę. Mimo iż wykonuje znacznie więcej operacji w pamięci to będą one odbywały się w cache procesora, gdzie są naprawdę szybkie.

Opis testu.
Wygeneruj N losowych liczb i wstaw je tak by zachowana została posortowana kolejność np. 5 1 4 2:
  • 5
  • 1 5
  • 1 4 5
  • 1 2 4 5

Usuń elementy z losowej pozycji np. dla 1 2 0 0, będzie to:
  • 1 2 4 5
  • 1 4 5
  • 1 4
  • 4
Na początek kilka funkcji pomocniczych. Pierwsza łączy nasz generator z rozkładem jednorodnym
TRnd get_rnd(const int max)
{
    const int seed = 858446;
    TBaseGeneratorType generator(seed);
    boost::uniform_int<> uni_dist(0, max);
    TRnd rnd(generator, uni_dist);
    return rnd;
}
Pierwsza część testu - generyczna funkcja do wstawiania, zachowując posortowaną kolejność.
template <typename T>
void insert_sort(T& con, const int size, TRnd& rnd)
{
    for (int i = 0; i < size; ++i) {
        const int value = rnd();
        typename T::iterator it = std::find_if(con.begin(), con.end(),
                                               value < _1);
        con.insert(it, value);
    }
}
Druga część testu - usuwanie wartości z losowej pozycji.
template <typename T>
void erase_by_rand_pos(T& con, TRnd& rnd)
{
    while (not con.empty()) {
        const int pos = rnd() % con.size();
        typename T::iterator it = con.begin();
        std::advance(it, pos);
        con.erase(it);
    }
}
Ciało testu:
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>

#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::lambda;
using namespace boost::posix_time;

typedef boost::mt19937 TBaseGeneratorType;
typedef boost::variate_generator<TBaseGeneratorType, 
                                boost::uniform_int<> > TRnd;

int main(int argc, char **argv)
{
    if (argc < 2) {
        cout << "Missing size argument" << endl;
        return -1;
    }

    TRnd rnd = get_rnd(std::numeric_limits<int>::max());
    const int size = boost::lexical_cast<int>(argv[1]);

    const ptime time_start = microsec_clock::local_time();
    cout << "Start at: " << time_start << ", size = "<< size << endl;

//    vector<int> container;
    list<int> container;
    insert_sort(container, size, rnd);

    const ptime time_insert = microsec_clock::local_time();
    cout << "Inserted sort:       " << time_insert - time_start << endl;

    erase_by_rand_pos(container, rnd);

    const ptime time_remove = microsec_clock::local_time();
    cout << "Erase at random pos: " << time_remove - time_insert << endl;
    cout << "Whole process:       " << time_remove - time_start << endl;

    return 0;
}
Wyniki. Wszystkie testy były kompilowane za pomocą g++ (4.6.3) z flagą -O2. Bjarne musiał korzystać z bardziej wyrafinowanego algorytmu niż mój, bo w jego wersji cały proces dla 200000 elementów zajął ok. 500 sekund, w moim przypadku było to już 1116 sekund (sic!), dlatego też nie testowałem powyżej tej wartości. Nie testowałem też "preallocated list" bo nie jestem pewien co to dokładnie jest.



I jeszcze jeden wykres, przedstawiający jak czasowo wyglądały poszczególne operacje, czyli wstawianie z wyszukiwaniem i usuwanie z wyszukiwaniem. Porównując dane, widać, że w przypadku listy, najwolniejszą operacją jest usuwanie z wyszukiwaniem, w przypadku wektora wstawianie z wyszukiwaniem. Usuwanie i wstawianie na wektorze powinny być jego słabym punktem, ale okazują się, że w połączeniu z wyszukiwaniem, są znacznie szybsze od tego co oferuje lista.



29 października 2012

StrictMock i referencja w konstruktorze [TRICK]

Napotkałem ostatnio taki problem (mowa tu o testach charakteryzacyjnych).

Przykład: Chcemy przetestować metodę Filter::run(). Klasa do działania potrzebuje obiektów Inc i Show. Nie mają one czysto abstrakcyjnych interfejsów (ale zrobimy dla nich mocki). Show potrzebuje do działania referencji do Inc.

Problem pojawia się, gdy mock zrobimy StrictMock-iem i będziemy chcieli go jeszcze zainicjować. Tutaj ShowMock chcemy zainicjować IncMock (linijka 49-50).
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/ref.hpp>
#include <gmock/gmock.h>

using namespace std;
using namespace ::testing;

class Inc {
public:
    virtual int inc(int v) { return v++; }
};

class IncMock : public Inc {
public:
    MOCK_METHOD1(inc, int(int));
};

class Show {
    Inc& obj;
public:
    Show(Inc& i) : obj(i) {}
    virtual void show(int v) { cout << "Val: " << obj.inc(v) << endl; }
};

class ShowMock : public Show {
public:
    ShowMock(Inc& i) : Show(i) {}
    MOCK_METHOD1(show, void(int));
};

class Filter {
    boost::shared_ptr<Show> sw;
    boost::shared_ptr<Inc> in;
public:
    Filter(boost::shared_ptr<Show> s, 
           boost::shared_ptr<Inc> i) : sw(s), in(i) {}
    void run(int val) {
        if (val > 5)
            sw->show(val);
        in->inc(val);
    }
};

TEST(FilterTest, test) {
    boost::shared_ptr<IncMock> l_incMock(new StrictMock<IncMock>());

//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (*l_incMock));
//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (boost::cref(*l_incMock)));

    boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
                                             (boost::ref(*l_incMock)));
    Filter l_filter(l_showMock, l_incMock);

    int l_val = 8;
    EXPECT_CALL(*l_incMock, inc(l_val)).WillOnce(Return(l_val + 1));
    EXPECT_CALL(*l_showMock, show(l_val));

    l_filter.run(l_val);
}

int main(int argc, char *argv[]) {
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
Pierwsze rozwiązanie wypluje error przy kompilacji
In file included from /home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock.h:64:0,
                 from main.cpp:4:
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h: In constructor ‘testing::StrictMock<M>::StrictMock(const A1&) [with A1 = IncMock, MockClass = ShowMock]’:
main.cpp:48:77:   instantiated from here
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: error: no matching function for call to ‘ShowMock::ShowMock(const IncMock&)’
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: note: candidates are:
main.cpp:28:5: note: ShowMock::ShowMock(Inc&)
main.cpp:28:5: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘Inc&’
main.cpp:26:7: note: ShowMock::ShowMock(const ShowMock&)
main.cpp:26:7: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘const ShowMock&’
Co się okazuje, StrictMock ma tylko konstruktory explicit, dlatego podziała trick z boost::ref().
template <typename A1>
explicit StrictMock(const A1& a1) : MockClass(a1) {
  ::testing::Mock::FailUninterestingCalls(
      internal::ImplicitCast_<MockClass*>(this));
}
Niestety nie wiem dlaczego nie działa boost::cref() ?!

16 października 2012

Boost.Random

Boost.Random na szybko:
* Wybieramy jeden z generatorów pseudolosowych oferowanych przez bibliotekę (linia 9).
* Inicjujemy go seed-em (linia 12).
* Wybieramy rozkład zmiennej losowej (w naszym przypadku jednorodny), wybieramy też przedział, z którego będą wybierane liczby (obustronnie domknięty od 1 do 6) (linia 13).
* Łączymy generator z rozkładem w postaci naszego obiektu (kostka) (linia 16).
#include <iostream>

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/variate_generator.hpp>

int main()
{
    typedef boost::mt19937 TBaseGeneratorType;

    const int seed = 1337;
    TBaseGeneratorType generator(seed);
    boost::uniform_int<> uni_dist(1, 6);

    typedef boost::variate_generator<TBaseGeneratorType, boost::uniform_int<> > TVar;
    TVar die(generator, uni_dist);

    std::cout << "Na kostce wypadło: " << die() << std::endl;

    return 0;
}
Wynik:
Na kostce wypadło: 2

15 października 2012

Benchmark framework (Boost.Chrono) - część II

Druga biblioteka, z której pomocą chciałem mierzyć czas to Boost.Chrono. Nie do końca wyczuwam różnice między nią a Boost.Data_Time. Z pewnością obie mają rzeczy, których nie posiada "konkurent". Np. Boost.Chrono pozwala na zdefiniowanie z jakiego zegara systemowego będziemy wykorzystywać informacje. Ciekawe, czy zegar, który pokazuje czas wykonania danego wątku, uwzględnia, to że proces mógł być zawieszony, albo wywłaszczony na długi czas? Hmmm, jeszcze się w to nie wgłębiałem.

Pierwsza sprawa, to paczka w Ubuntu (libboost-all-dev), która ciągle wskazuje na wersję 1.46, a tam Boost.Chrono brakuje. Musiałem, wymienić to na:
apt-get install libboost1.48-all-dev
Druga spraw, to linkowanie. Chrono, korzysta z Boost.System i obie biblioteki trzeba zlinkować z programem (tu mam wątpliwości czy istnieje wersja, której nie trzeba linkować - nie doczytałem, korzystam z tego co mam w paczkach Ubuntu).

Zmiany w CMakeList.txt dla mojego projektu
project(benchmark_container_iterator)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})

find_package(Boost COMPONENTS system chrono REQUIRED)
target_link_libraries(${PROJECT_NAME}
  ${Boost_SYSTEM_LIBRARY}
  ${Boost_CHRONO_LIBRARY}
)
Dla benchmarka chciałem użyć process_cpu_clock, ale jego precyzja była do kitu. Jednak według opisu, wydaje się tym czego szukam:
Process and thread clocks are used usually to measure the time spent by code blocks, as a basic time-spent profiling of different blocks of code (Boost.Stopwatch is a clear example of this use).
Ostatecznie padło na "steady_clock"

Zasada działania, jest podobna jak w wersji poprzedniej. To bardzo proste wykorzystanie możliwości tej biblioteki.
#include <list>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>

#include <boost/chrono.hpp>

using namespace std;
using namespace boost::assign;

int main()
{
    using namespace boost::chrono;
    steady_clock::time_point time1 = steady_clock::now();
    cout << "Start at:              " << time1 << endl;

    const int size = 400500;
    list<int> l = list_of(1337).repeat(size, 1337);

    steady_clock::time_point time2 = steady_clock::now();
    nanoseconds td_init = duration_cast<nanoseconds>(time2 - time1);
    cout << "Memory Initialization: " << td_init << endl;

    int result = 0;
    BOOST_FOREACH(int value, l) {
        if (value > result)
            result = value;
    }

    steady_clock::time_point time3 = steady_clock::now();
    nanoseconds td_find = duration_cast<nanoseconds>(time3 - time2);
    cout << "Searching:             " << td_find << endl;

    return result;
}
Wyniki:
Start at:              14714172071593 nanoseconds since boot
Memory Initialization: 128273372 nanoseconds
Searching:             29473710 nanoseconds

14 października 2012

Benchmark framework (Boost.Date_Time)

Pisząc test porównujący czas wykonania dwóch algorytmów, natchnęło mnie by zrezygnować z wołania za pomocą metody system() polecenia wyświetlającego czas i skorzystać jakiejś biblioteki.
#include <iostream>
#include <stdlib.h>

int main()
{
    system("date +%M.%S.%N");
    return 0;
}
W boost znalazłem dwie, które nadawały by się do moich potrzeb. Niestety w żadną nie zagłębiałem się dokładnie. Pierwsza to Boost.Data_Time, druga to Boost.Chrono. Tutaj będzie tylko o pierwszej.

Biblioteka, pozwala na pobieranie czasu, obliczanie czasu trwania i przesunięć w czasie. Wszystko to dla różnych systemów kalendarzowych np. UTC, kalendarz gregoriański. Pozwala też zapisywać i odczytywać datę w różnych formatach - z tego co czytałem, bo nie miałem czasu testować.

W tym programie pobieram trzykrotnie lokalny czas (UTC) z dokładnością co do milisekund (linijki 14, 20 i 30). Następny krok to obliczenie czasu trwania (linijki 21 i 31) inicjalizowania listy, oraz jej przeszukiwania.
#include <list>
#include <iostream>
#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>

#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::assign;

int main()
{
    using namespace boost::posix_time;
    ptime time1 = microsec_clock::local_time();
    cout << "Start at:              " << time1 << endl;

    const int size = 400500;
    list<int> l = list_of(1337).repeat(size, 1337);

    ptime time2 = microsec_clock::local_time();
    time_duration td_init = time2 - time1;
    cout << "Memory Initialization: " << td_init << endl;

    int result = 0;
    BOOST_FOREACH(int value, l) {
        if (value > result)
            result = value;
    }

    ptime time3 = microsec_clock::local_time();
    time_duration td_find = time3 - time2;
    cout << "Searching:             " << td_find << endl;

    return result;
}
Wynik
Start at:              2012-Oct-14 17:13:36.716994
Memory Initialization: 00:00:00.125376
Searching:             00:00:00.021398

12 października 2012

SetArgReferee w DoAll

Czasami kod z którym mamy do czynienia, przekazuje nam wynik swojego działa zapisując go do argumentu przekazanego przez referencję. Chciało by się żeby wyglądało to troszkę schludniej, jednakże z powodów optymalizacyjnych (C++11 wprowadza coś takiego jak move constructor), trzeba z tym żyć, no i czasami testować. Google Mock wychodzi na przeciw, dostarczając m.in. metodę SetArgReferee() (jest też wersja dla wskaźników).

Poniżej będziemy testować klasę Simple, która posiada jedną metodą do zwracania pierwszego elementu wektora - returnFirst().

Przez "dependency injection", do klasy wstrzykiwany jest obiekt, który zadziała na wektorze (obróci go) nim zostanie zwrócony ten pierwszy (czyli ostatni element). Ten obiekt sobie z-mockujemy i tutaj przyda nam się SetArgReferee.
class IVecReverse {
public:
    virtual bool myReverse(std::vector<int>& v) const = 0;
};

class VecReverse : public IVecReverse {
public:
    bool myReverse(std::vector<int>& v) const {
        if (not v.empty()) {
            std::reverse(v.begin(), v.end());
            return true;
        }
        return false;
    }
};

class Simple {
public:
    Simple(IVecReverse& vs) : VecReverse(vs) {}

    int returnFirst(std::vector<int>& v) {
        if (VecReverse.myReverse(v))
            return v[0];
        return -1;
    }
protected:
    IVecReverse& VecReverse;
};
A teraz test. A więc z-mockowana metoda myReverse(), powinna przyjąć jakiś wektor (v1), który na końcu ma być odwrócony (to będzie nasze v2). Całą tą akcję trzeba zamknąć w DoAll(). W nawiasach <> dla SetArgReferee, wskazujemy numer parametru, dla którego robimy podmianę (mamy jeden argument więc będzie zero).
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

using namespace boost::assign;
using namespace ::testing;

class VecReverseMock : public IVecReverse {
public:
    MOCK_CONST_METHOD1(myReverse, bool (std::vector<int>&));
};

class SimpleTest : public Test {
public:
    VecReverseMock vrMock;
};

TEST_F(SimpleTest, testReturnFirst)
{
    Simple simple(vrMock);
    std::vector<int> v1 = boost::assign::list_of(3)(2)(4);
    std::vector<int> v2 = boost::assign::list_of(4)(2)(3);

    // Return musi byc ostatnie
    EXPECT_CALL(vrMock, myReverse(v1))
            .WillOnce(DoAll(SetArgReferee<0>(v2), Return(true)));
    EXPECT_EQ(4, simple.returnFirst(v1));
}

int main(int argc, char *argv[])
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
A teraz krótko o DoAll(a1, a2, ..., an) - jego zadaniem jest wykonanie wszystkich akcji od "a1" do "an", wszystkie poza "an" powinny zwracać void, "an" musi coś zwrócić bo taki będzie wynik całego DoAll (w naszym przypadku jest to Return(true)).
[----------] 1 test from SimpleTest
[ RUN      ] SimpleTest.testReturnFirst
[       OK ] SimpleTest.testReturnFirst (1 ms)
[----------] 1 test from SimpleTest (1 ms total)

9 października 2012

Boost.Assignment - list_of

Biblioteka, której celem jest łatwe wypełnianie kontenerów: http://www.boost.org/doc/libs/1_51_0/libs/assign/doc/index.html

Do wypełnienia większości kontenerów możemy posłużyć się metodą list_of(), w przypadku mapy będzie to metoda map_list_of(), a krotki tuple_list_of().
#include <vector>
#include <list>
#include <map>
#include <string>
#include <iostream>
#include <boost/foreach.hpp>

#include <boost/assign/list_of.hpp>

int main()
{
    std::vector<int> vec = boost::assign::list_of(45)(78);
    std::list<int> lst = boost::assign::list_of(105)(108);

    BOOST_FOREACH(int& value, vec) {
        std::cout << "Value: " << value << std::endl;
    }

    BOOST_FOREACH(int& value, lst) {
        std::cout << "Value: " << value << std::endl;
    }

    typedef std::map<std::string, int> map_type;
    map_type mp = boost::assign::map_list_of("first", 1)("second", 2);

    BOOST_FOREACH(const map_type::value_type& value, mp) {
        std::cout << "Value: " << value.first << std::endl;
    }

    return 0;
}
Wynik:
Value: 45
Value: 78

Value: 105
Value: 108

Value: first
Value: second
Biblioteka posiada dodatkowo szereg innych przydatnych funkcji: przeciążony operator +=, który umożliwia dopisywanie się na końcu wektora, insert() dostawianie dodatkowych elementów do mapy, albo repeat(), który kilkukrotnie powtarza wstawianie danej wartości, itp.
#include <boost/assign/std/vector.hpp> // dla'operator+=()'
#include <boost/assign/std/list.hpp>
#include <boost/assign/list_inserter.hpp> // dla 'insert()'

using namespace boost::assign; // dla wygody korzystania z +=

// ...

vec += 5, boost::assign::repeat(3, 69), 6;
lst += 8, 9;
insert(mp)("aaa", 3)("bbb", 4);
Wynik:
Value: 45
Value: 78
Value: 5
Value: 69
Value: 69
Value: 69
Value: 6

Value: 105
Value: 108
Value: 8
Value: 9

Value: aaa
Value: bbb
Value: first
Value: second

28 września 2012

Konstruktor a SetUp (Google Test)

Chciałem się dowiedzieć jak właściwie działa metoda SetUp(), dla klas testów w konfrontacji z konstruktorem takiej klasy.
#include <gmock/gmock.h>
#include <stdio.h>

using namespace ::testing;

class SimpleTest : public Test
{
public:
    SimpleTest() {
        std::cout << "Constructor()" << std::endl;
    }

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

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

    ~SimpleTest() {
        std::cout << "Destructor()" << std::endl;
    }
};

TEST_F(SimpleTest, test1)
{
    std::cout << "test1 " << std::endl;
}

TEST_F(SimpleTest, test2)
{
    std::cout << "test2 " << std::endl;
}

int main(int argc, char *argv[])
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
Najwyraźniej każde makro TEST_F, tworzy klasę dziedziczącą z naszej klasy testującej. SetUp() oraz Constructor() wołane są za każdym razem przed rozpoczęciem testu. Odpowiednio TearDown() oraz Destructor(), także wołane są na końcu każdego testu.
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from SimpleTest
[ RUN      ] SimpleTest.test1
Constructor()
SetUp()
test1 
TearDown()
Destructor()
[       OK ] SimpleTest.test1 (0 ms)
[ RUN      ] SimpleTest.test2
Constructor()
SetUp()
test2 
TearDown()
Destructor()
[       OK ] SimpleTest.test2 (0 ms)
[----------] 2 tests from SimpleTest (1 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (4 ms total)
[  PASSED  ] 2 tests.

GoogleTest/Mock instalacja

Piekło zamarzło, nie udało mi się zainstalować narzędzi "Google Mock" i "Google Test" z repozytorium Ubuntu ("google-mock", "libgtest-dev"). Tzn. udało się, ale nijak nie zrozumiałem jak z linkować to co dostałem z moim projektem/testem. Według nich powinienem zadziałać skryptem (gtest-config), tylko gdzie go można znaleźć...?
g++ $(gtest-config --cppflags --cxxflags) -o foo.o -c foo.cpp
g++ $(gtest-config --ldflags --libs) -o foo foo.o
W ramach kompilacji chciałem przeprowadzić standardowe budowanie ze skryptów make. Wpierw okazało się, że autoconf/make(?) nie jest już wspierany (albo odradzany), i że powianiem skorzystać z CMake. Tak też uczyniłem, lecz to co zostało wyplute, nijak nie chciało współgrać z linkerem.

Skończyło się ręcznym kompilowaniu bibliotek. Wpierw ustawiłem sobie kilka zmiennych środowiskowych, żeby było bardziej przejrzyście.
export GMOCK_DIR=~/Downloads/gmock-1.6.0/
export GTEST_DIR=~/Downloads/gmock-1.6.0/gtest/
export GMOCK_INCLUDE=~/Downloads/gmock-1.6.0/include/
export GTEST_INCLUDE=~/Downloads/gmock-1.6.0/gtest/include/
export GMOCK_LIB=~/Downloads/gmock-1.6.0
Kompilacja do statycznych bibliotek
g++ -I${GTEST_INCLUDE} -I${GTEST_DIR} -I${GMOCK_INCLUDE} \
    -I${GMOCK_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
g++ -I${GTEST_INCLUDE} -I${GTEST_DIR} -I${GMOCK_INCLUDE} \
    -I${GMOCK_DIR} -c ${GMOCK_DIR}/src/gmock-all.cc
ar -rv libgmock.a gtest-all.o gmock-all.o
Prosty test:
#include <gtest/gtest.h>

using namespace testing;

int main()
{
    EXPECT_EQ(1, 2);
    return 0;
}
I wreszcie kompilacja testu:
g++ -I${GTEST_INCLUDE} -I${GMOCK_INCLUDE} \
    main.cpp ${GMOCK_LIB}/libgmock.a -lpthread -o main
Oraz wynik:
$ ./main 
main.cpp:7: Failure
Value of: 2
Expected: 1

Zasada Pareto

http://pl.wikipedia.org/wiki/Zasada_Pareto



Drugą cześć zasady sobie dopowiedziałem. A tymczasem Hello Google Chart Tools.

19 września 2012

FL like Flame

Ciekawa analiza serwera C&C obsługującego Flame-a:
http://www.securelist.com/en/blog/750/Full_Analysis_of_Flame_s_Command_Control_servers

Zauważyłem, że dla initialization vector (IV) została wybrana nielosowa wartość (co jest generalnie zalecane).
IV: 12345678
Zazwyczaj w systemach kryptograficznych wszyscy drżą, o każdy detal, który mógłby osłabić całość. Tutaj nie ma to żadnego znaczenia, skoro ten klucz już ktoś odczytał. Ponadto w trybie CBC, klucz ten nie musi być utrzymywany w tajemnicy.

Druga sprawa, to złamanie hasła za-hashowanego za pomocą MD5.
Password hash (MD5): 27934e96d90d06818674b98bec7230fa

(ok, cracked: 900gage!@#)
Nie jest to obecnie zalecana metoda tworzenie funkcji skrótu. Jak widać dość skomplikowane 10 literowe, zostało odkryte, choć nie ma informacji, czy czy to dzięki słabościom MD5, czy może era tak krótkich haseł już minęła.

Takie tam ... dzień bez, zawiści, dniem straconym ;)

17 września 2012

Kopiowanie kluczy mapy

Szukałem ładnego sposobu na wydobycie kluczy z mapy, tak by nie tworzyć pętli for iterującej po mapie i przepisującej wartość it->first do jakiegoś kontenera. Na szczęście okazało się, że biblioteka boost oferuje gotowy przykład:
http://www.boost.org/doc/libs/1_43_0/libs/range/doc/html/range/reference/adaptors/reference/map_keys.html

Jest to zastosowanie wzorca projektowego "adapter". Wykorzystuje on i przekształca istniejący interfejs w taki jakiego oczekuje klient. Tutaj Range iterator zostaje owrapowany w ten sposób, że nowy iterator, zwraca to czego oczekujemy.
#include <boost/range/adaptor/map.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
#include <boost/foreach.hpp>
#include <iostream>
#include <map>
#include <vector>

int main()
{
    std::map<std::string, int> m;
    m["first"] = 301;
    m["second"] = 302;
    m["third"] = 54;

    std::vector<std::string> v;
    boost::push_back(v, m | boost::adaptors::reversed
                          | boost::adaptors::map_keys);

    boost::push_back(v,
                     boost::adaptors::keys(
                         boost::adaptors::reverse(m)));

    BOOST_FOREACH(std::string& s, v)
    {
        std::cout << s << std::endl;
    }

    return 0;
}
Istnieją dwa sposoby na stworzenia adaptera, pierwsza z wykorzystaniem operator|(), druga to skorzystanie z metod. Wygląda na to, że kolejność nakładanie adapterów nie ma znaczenia. Ale to trzeba doczytać, albo przetestować jeszcze.

Dodatkowo wszystkie klucze wpisuje w odwrotnej kolejności. A oto wynik
third
second
first
third
second
first

15 września 2012

Boost.Tuple jako klucz mapy

Ostatnio próbowałem wykorzystać obiekt krotki, jako klucz std::map. Jak się okazało kompilator zaprotestował
#include <string>
#include <map>
#include <boost/tuple/tuple.hpp>

int main()
{
    std::map<boost::tuple<int, bool, std::string>, int> m;

    boost::tuple<int, bool, std::string> key(2, false, "asdf");
    m[key] = 44;

    return 0;
}
Protest - kompilator skarży się, że boost::tuple (nasz klucz) nie posiada operatora less.
/usr/include/c++/4.6/bits/stl_function.h:236:22: error: no match for ‘operator<’ in ‘__x < __y’
Po zbadaniu problemu okazało się, że trzeba do-includować poniższą linijkę.
#include <boost/tuple/tuple_comparison.hpp>

14 września 2012

Boost.Foreach

Często w książkach można spotkać konstrukcję pętli for, której zadaniem jest prze-iterowanie po jakimś kontenerze. Wtedy warunek zakończenia pętli wygląda najczęściej tak:
it != v.end()
Jeżeli jednak nasz kod z pętli nie usuwa, dodaje, przemieszcza (?) obiektów w kontenerze, można pokusić się o bardziej optymalne rozwiązanie, bowiem metoda end(), będzie wołana na początku każdego rozpoczęcia pętli.
#include <iostream>
#include <vector>
#include <boost/foreach.hpp>

template <typename T>
class Vec : public std::vector<T>
{
public:
    typename std::vector<T>::iterator begin()
    {
        std::cout << "begin()" << std::endl;
        return std::vector<T>::begin();
    }
    typename std::vector<T>::iterator end()
    {
        std::cout << "end()" << std::endl;
        return std::vector<T>::end();
    }
};

int main()
{
    Vec<int> v;
    v.push_back(10);
    v.push_back(45);
    v.push_back(6);

    for (Vec<int>::iterator it = v.begin(); it != v.end(); ++it)
    {
        std::cout << "value: " << *it << std::endl;
    }

    return 0;
}
Wynik
begin()
end()
value: 10
end()
value: 45
end()
value: 6
end()
Wypadało by więc, zapisać sobie na boku wartość zwracaną przez end i z takiej wartości korzystać podczas sprawdzania warunku końca pętli. Minus - kod strasznie puchnie - rozwiązanie BOOST_FOREACH()
int main()
{
    Vec<int> v;
    v.push_back(10);
    v.push_back(45);
    v.push_back(6);

    BOOST_FOREACH(int i, v)
    {
        std::cout << "value " << i << std::endl;
    }

    return 0;
}

A oto wynik - z jednym wołaniem metody end()
begin()
end()
value 10
value 45
value 6

26 sierpnia 2012

C struct - inicjalizacja zerami

Dowiedziałem się (http://www.ex-parrot.com/~chris/random/initialise.html), że można zainicjować całą strukturę zerami, korzystają z zapisu jak poniżej. Obejmuje to również zagnieżdżone pola.
struct SAaa {
    int i;
    int w;
};

int main() {
    struct SAaa aaa = { 0 };
    return 0;
}
Gdzieś doczytałem, że gdy w miejsce zera wstawię coś innego, to efekt będzie odmienny.
int main() {
    struct SAaa aaa = { 5 };
    return 0;
}
Kompilacja, bez flag optymalizacji:
$ g++ main.cpp -m32 -masm=intel -S
I to co wyprodukuje kompilator:
.file   "main.cpp"
    .intel_syntax noprefix
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    push    ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    mov     ebp, esp
    .cfi_def_cfa_register 5
    sub     esp, 16
    call    __x86.get_pc_thunk.ax
    add     eax, OFFSET FLAT:_GLOBAL_OFFSET_TABLE_
    mov     DWORD PTR -8[ebp], 0
    mov     DWORD PTR -4[ebp], 0
    mov     DWORD PTR -8[ebp], 5
    mov     eax, 0
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .section    .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
    .globl  __x86.get_pc_thunk.ax
    .hidden __x86.get_pc_thunk.ax
    .type   __x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
.LFB1:
    .cfi_startproc
    mov     eax, DWORD PTR [esp]
    ret
    .cfi_endproc
.LFE1:
    .ident  "GCC: (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005"
    .section    .note.GNU-stack,"",@progbits
Cóż, tego się nie spodziewałem - pewnie dlatego, że nigdy nie przebiłem się przez standard. Pierwsze pole dostaje piątkę, reszta nadal inicjowana jest zerami.
W dodatku (przez brak optymalizacji przy kompilacji), zapis do aaa.i będzie dwukrotny:
- aaa.i wypełnione "0" (linia 17)
- aaa.i wypełnione "5" (linia 19)

25 sierpnia 2012

Życie w scrum-ie

Metodologia scrum-owa, jest pełna wspaniałych frazesów i jestem w stanie uwierzyć, że gdzieś tam istnieje sobie taka utopijna wyspa, gdzie jej zasady się sprawdzają. Mając z nią od jakiegoś czasu styczność, stwierdzam, że obowiązkowym aktorem w całym tym procesie powinien być psychiatra.

Przynajmniej w polskich warunkach, jest to coś co zawsze będzie spotykało niesamowity opór. Jesteśmy przyzwyczajani do bycia indywidualistami. Mimo wszystko instynkt podpowiada, że potrzeba nam wyraźnej hierarchii jak w stadzie. Lubimy awansować w jej szeregach, czy to przez strącenie kogoś z góry, czy to przez walkę z kimś na dole. A to wszystko, ta cała piramidka, bez przywódcy, bo przywódca jest wrogiem wszystkich, pierwszym do wyeliminowania.

Najwyraźniej każdy z nas dąży do autodestrukcji...