30 kwietnia 2013

Constructor() = default/delete;

Podstawy

W poprzedniej wersji C++, jeżeli klasa potrzebowała explicit konstruktorów np. domyślnego, który nic nie robił, trzeba było napisać coś w tym stylu:
class MyClass 
{ 
public: 
    MyClass() {}
};
Zalecane jest jednak by interfejs zawierał deklaracje publicznych metod bez ich implementacji. Rozwiązaniem jest przeniesienie definicji do pliku cpp, jednak to tylko niepotrzebnie zwiększa ilość kodu.
MyClass::MyClass() {}
W nowym standardzie, kompilator wygeneruje za nas domyślny konstruktor bez potrzeby pisania implementacji, jeśli tylko użyjemy słowa default. Podobnie możemy korzystać z delete, gdy nie chcemy jakiegoś konstruktora dla naszej klasy, oraz nie chcemy by kompilator wygenerował konstruktor domyślny. Musimy więc "wykasować" konstruktor domyślny.
#include <iostream>

class MyClass {
public:
    MyClass() = default;
    MyClass(const MyClass& r) = delete;
    MyClass(int v) : value(v) {}

    int value;
};

int main()
{
    MyClass m1;         // default constructor
    MyClass m2(44);
//  MyClass m3 = m2;    // copy constructor - Error z powodu "delete"

    std::cout << "m1 = " << m1.value << std::endl;
    std::cout << "m2 = " << m2.value << std::endl;
//  std::cout << "m3 = " << m3.value << std::endl;

    return 0;
}
Wyniki:
m1 = 134515449
m2 = 44
Możemy też w ten sposób stworzyć operatora przypisania, który domyślnie zrobi płytką kapię zawartości obiektu.
#include <iostream>
#include <vector>

using namespace std;

class MyClass {
public:
    MyClass() = default;
    MyClass(const MyClass& r) = delete;
    MyClass& operator=(const MyClass& r) = default;

    void put(int value) {
        vec.push_back(value);
    }

    void show() const {
        for (const auto& v : vec)
            cout << v << " ";
        cout << endl;
    }

private:
    vector<int> vec;
};

int main()
{
    MyClass m1;
    m1.put(1);
    m1.put(2);

    MyClass m2;
    m2 = m1;
    m2.show();

    return 0;
}
Wynik:
1 2 
Operacje typu default traktowane są jako user-declared, więc np. klasa bez deklaracji destruktora nie jest taka sama jak klasa z destruktorem default. Przykład poniżej.

Można także usunąć funkcję, wtedy zabraniamy niejawnej konwersji argumentów w momencie jej zawołania.
#include <iostream>

bool convert(int val) {
    return true;
}

bool convert(double vale) = delete;

int main()
{
    convert(44);
    convert(12.3); // Error
    return 0;
}

Kiedy kompilator generuje za nas kod

Zasady automatycznego generowania konstruktorów domyślnych/kopiujących, operatorów przypisani/przesunięcia i destruktorów przez kompilator:
  • Jeżeli konstruktor domyślny, konstruktor kopiujący, operator przypisania albo destruktor w klasie bazowej jest skasowany (delete) lub niedostępny (np. przez private), wtedy odpowiadający mechanizm w klasie pochodnej zostanie zdefiniowany jako skasowany (delete).
  • Jeżeli klasa bazowa ma niedostępny albo skasowany (delete) destruktor wtedy zsyntetyzowany konstruktor domyślny i konstruktor kopiujący w klasie pochodnej są zdefiniowane jako delete.
  • Jeżeli klasa bazowa ma niedostępny albo skasowany (delete) destruktor wtedy move konstruktor zostanie zdefiniowany jako delete.
  • Jeżeli operacje move, w klasie bazowej są skasowana albo niedostępna, to pomimo użycia default w klasie pochodnej, odpowiednie operacje nadal będą skasowane (ponieważ składowych klasy bazowej nadal nie można przenieść).

Wirtualny destruktor = default

Typowym sposobem tworzenia interfejsów w C++ jest wykorzystanie klas czysto wirtualnych (Base). Specyfika języka wymaga, aby w klasie takiej znalazł się również zdefiniowany wirtualny destruktor. Teraz zamiast tworzyć jego definicję można posłużyć się default.
struct Base {
    virtual ~Base() = default;

    virtual void fun() = 0;
};

struct Child : public Base {
    void fun() override { cout << "Child::fun" << endl; }
    Data data;
};
Nasuwa się pytanie, na które wciąż nie mam wystarczająco dobrej odpowiedzi. Czy user-declared (default) destruktor sprawi, że nie będzie można skorzystać z move semantic? Stworzyłem kilka eksperymentów i spróbowałem sobie zracjonalizować, dlaczego uzyskałem takie, a nie inne wyniki. W pierwszej kolejności potrzebna jest gadatliwa klasa:
#include <iostream>

using namespace std;

struct Data {
    Data() = default;
    Data(const Data&) {
        cout << "Copy constructor" << endl;
    }
    Data(Data&&) noexcept {
        cout << "Move constructor" << endl;
    }
    Data& operator=(const Data&) {
        cout << "Copy assign" << endl;
        return *this;
    }
    Data& operator=(Data&&) noexcept {
        cout << "Move assign" << endl;
        return *this;
    }
    ~Data() = default;
};
Dziedziczenie "nie uszkadza" (nie kasuje) mechanizmów move semantic w klasie pochodnej. No prawie. Ponieważ klasa Base1 jest czysto wirtualna i nie posiada żadnych danych, więc nie ma też sensu, by narzucać jakieś ograniczenia klasie z niej dziedziczącej w kwestii kopiowania lub przesuwania.
struct Base1 {
    virtual ~Base1() = default;

    virtual void fun() = 0;
};

struct Child1 : public Base1 {
    void fun() override { cout << "Child1::fun" << endl; }
    Data data;
};

int main() {
    Child1 c1;
    auto m1 = std::move(c1);
}
Bez problemu zadziała konstruktor move. Wynik:
Move constructor
Co się jednak stanie, gdy dane zostaną przeniesione do klasy Base (Base2)?
struct Base2 {
    virtual ~Base2() = default;

    virtual void fun() = 0;
    Data data;
};

struct Child2 : public Base2 {
    void fun() override { cout << "Child2::fun" << endl; }
};

int main() {
    Child2 c2;
    auto m2 = std::move(c2);
}
Wynik:
Copy constructor
Co ciekawe teraz move semantic już nie działa, za to działa kopiowanie. W pierwszej kolejności myślałem, że user-declared destruktor kasuje konstruktor move, gdy tylko w klasie znajdują się jakieś dane, ale to dalej nie jest do końca prawda.

Jeżeli user-declared destruktor kasowałby konstruktor move, to kod w którym konstruktor move jest explicit wykasowany (delete) powinien zachowywać się w identyczny sposób. W przykładzie poniżej Base3 się jednak nie skompiluje. Kompilator poinformuje o braku konstruktora move, ale także o braku konstruktora kopiującego. Musiałem więc stworzyć znacznie bardziej rozbudowaną wersją (Base4) z wykasowanym (delete) operatorem move i default-owym konstruktorem kopiującym.
//struct Base3 {
//    Base3(Base3&&) = delete;        // Error
//    virtual ~Base3() = default;
//
//    virtual void fun() = 0;
//    Data data;
//};

//struct Child3 : public Base3 {
//    void fun() override { cout << "Child3::fun" << endl; }
//};

struct Base4 {
    Base4() = default;

    Base4(Base4&&) = delete;
    Base4(const Base4&) = default;
    virtual ~Base4() = default;

    virtual void fun() = 0;
    Data data;
};

struct Child4 : public Base4 {
    void fun() override { cout << "Child4::fun" << endl; }
};

int main() {
//  Child3 c3;
//  auto m3 = std::move(c3);       // Error

    Child4 c4;
    auto m4 = std::move(c4);
}
Wynik:
Copy constructor
W takim razie skoro user-declared destruktor kasuje move konstruktor, to dlaczego nie kasuje także konstruktora kupującego, w końcu w Base4 wykasowany move konstruktor właśnie to uczynił? Najlepsza odpowiedź jaką uzyskałem, to taka, że default destruktor nie tyle kasuje move konstruktor, ale zapobiega jego deklaracji. Mało to intuicyjne jak dla mnie.

Jak widać reguły jakimi rządzi się ten mechanizm są bardzo skomplikowane. Jak więc powinna wyglądać prawidłowo taka klasa. Zalecaną metodą jest deklaracja wszystkich pięciu elementów: The rule of three/five/zero. Jeżeli potrzebujesz choćby jednego zadeklaruj wszystkie.
struct Base0 {
    Base0() = default;

    Base0(Base0&&) = default;
    Base0(const Base0&) = default;
    Base0& operator=(const Base0&) = default;
    Base0& operator=(Base0&&) = default;
    virtual ~Base0() = default;

    virtual void fun() = 0;
    Data data;
};

struct Child0 : public Base0 {
    void fun() override { cout << "Child0::fun" << endl; }
};

int main() {
    Child0 c0;
    auto m0 = std::move(c0);
}
Wynik:
Move constructor

Na koniec jeszcze jeden przypadek, którego już zupełnie nie rozumiem. Jest to zmodyfikowana wersja klasy Base4. Nie jest ona jednak już czysto wirtualna (funkcja fun ma definicję), można więc tworzyć obiekty tej klasy.
struct BaseX {
    BaseX() = default;

    BaseX(BaseX&&) = delete;
    BaseX(const BaseX&) = default;
    virtual ~BaseX() = default;

    virtual void fun() { cout << "BaseX::fun" << endl; }
    Data data;
};

struct ChildX : public BaseX {
    void fun() override { cout << "ChildX::fun" << endl; }
};

int main() {
    BaseX bx;
    auto mb = std::move(bx);    // Error

    ChildX cx;                  // To samo co Child4
    auto mc = std::move(cx);    
}
ChildX niczym nie różnic się od Child4 i tak jak poprzednio std::move zwoła konstruktor kopiujący. Kompilator jednak zaprotestuje, gdy będziemy chcieli zawołać std::move na obiekcie BaseX. Co prawda nie ma konstruktora move, ale dlaczego w zamian nie zostanie zawołany konstruktor kopiujący?
main.cpp: In function ‘int main()’:
main.cpp: error: use of deleted function ‘BaseX::BaseX(BaseX&&)’
        auto mb = std::move(bx);
                              ^
main.cpp: note: declared here
        BaseX(BaseX&&) = delete;
        ^~~~~~

29 kwietnia 2013

Firefox addon - run calc.exe

Przydatna rzecz - wtyczka dla Firefox-a, choć praca nad nią była dość niewygodna. W planach miałem, zbudowanie czegoś podobnego do wtyczki RefControl, która kontroluje co jest wysyłane jako HTTP Referer do strony. Na razie jednak, zacząłem od czegoś prostszego. Do zbudowania wtyczki, posłużył mi Add-on builder:
Poniższy przykład nie robi nic użytecznego. Dopisuje do każdej strony "Hello world" (na jej początku), zapisuje w konsoli vendor-a, a po naciśnięciu ikony widgetu otwiera w nowej zakładce stronkę dupa.pl i uruchamia kalkulator (to jest wersja pod Windows). Poniżej przydatne linki, z dokumentacją do wykorzystanych bibliotek:
Dość często miała problemy ze zdiagnozowanie przyczyny, dlaczego wtyczka się nie "kompiluje". A gdy "kompilacja" już się powiodła, często znikała ikona mojego widget-u. Rozwiązaniem drugiego problemu, było częste zmienianie jego identyfikatora. Bez wątpienia, potrzebna jest pewna wprawa, żeby pisanie następnych mogło odbywać się sprawniej.
// The main module of the niegodziwyberu Add-on.

// Modules needed are `require`d, similar to CommonJS modules.
// In this case, creating a Widget that opens a new tab needs both the
// `widget` and the `tabs` modules.
var Widget = require("widget").Widget;
var tabs = require('tabs');
var { Cc, Ci } = require('chrome');

exports.main = function() {
    // https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/system.html#vendor
    var system = require("sdk/system");
    console.log("Console print, vendor = " + system.vendor);

    // https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/modifying-web-pages-url.html
    // Import the page-mod API
    var pageMod = require("sdk/page-mod");

    // Create a page mod
    // It will run a script whenever a ".pl" URL is loaded
    // The script replaces the page contents with a message
    pageMod.PageMod({
        include: "*.pl",
        contentScript: 'content.document.body.innerHTML = ' +
                       '"<h1>Hello World</h1>" + content.document.body.innerHTML;'
    });

    // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIProcess#See_also
    // ZACZEŁO DZIAŁAĆ, PO PRZENIESIENIU TUTAJ
    // create an nsIFile for the executable
    var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);

    // create an nsIProcess
    var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);

    // https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/widget.html
    new Widget({
        // Mandatory string used to identify your widget in order to
        // save its location when the user moves it in the browser.
        // This string has to be unique and must not be changed over time.
        // DA SIĘ COŚ ZROBIĆ, ŻEBY TEGO CAŁY CZAS NIE POPRAWIAĆ?
        id: "niegodziwier-widget-7",

        // A required string description of the widget used for
        // accessibility, title bars, and error reporting.
        label: "NiegodziwyBeru Widget",

        // An optional string URL to content to load into the widget.
        // This can be local content or remote content, an image or
        // web content. Widgets must have either the content property
        // or the contentURL property set.
        //
        // If the content is an image, it is automatically scaled to
        // be 16x16 pixels.
        contentURL: "http://www.mozilla.org/favicon.ico",

        // Add a function to trigger when the Widget is clicked.
        onClick: function(event) {
            // Open a new tab in the currently active window.
            tabs.open("http://dupa.pl");

            // Open calc application
            file.initWithPath("c:\\windows\\system32\\calc.exe");
            process.init(file);
            var args = [];
            process.run(false, args, args.length);
        }
    });

};

21 kwietnia 2013

Ataki "na" i "z" JavaScript

Dwie bardzo fajne prezentacje, które udało mi się ostatnio obejrzeć. Pierwsza, dotyczy ataków na kompilatory JIT, np. JavaScript-owy silnik w przeglądarce.
Druga pokazuje, kto tak naprawdę korzysta z "anonimowych proxy" ;) i jak wstrzykiwać złośliwi kod JavaScript do stron, która przez taki serwer przechodzą.
Przy okazji, dowiedziałem się o ciekawym projekcie. Może kiedyś będzie szansa się w to zagłębić:

14 kwietnia 2013

trailing return type

W nowym standardzie, został zaproponowany nowy sposób oznaczania zwracanego typu z metod/funkcji. Przydatne linki:
Najpierw konfrontacja starego i nowego stylu:
#include <iostream>

void a1(int) {
    std::cout << "call a1()" << std::endl;
}

auto a2(int) -> void {
    std::cout << "call a2()" << std::endl;
}

int main() {
    a1(1);
    a2(2);
    return 0;
}
Obie funkcje robią dokładnie to samo. Jak to zwykle bywa w C++ o mocy nowego mechanizmu można się przekonać, badając pewne szczególne przypadki tworzenia metod szablonowych. Dotychczas język nie potrafił poradzić sobie np. z detekcją typu, jaki ma powstać w wyniku dodania dwóch wartości typów pozyskanych z szablonu. W poniższym przykładzie add3(), korzysta z już nowych właściwości języka. Warto podkreślić, że słowo kluczowe auto, jest tutaj czymś zupełnie innym, niż automatyczną detekcją typu.
#include <iostream>

template<class Lhs, class Rhs>
Ret add1(const Lhs &l, const Rhs &r)
{
    return l + r;   // Error, Ret musi być typu l+r
}

template<class Lhs, class Rhs>
decltype(l+r) add2(const Lhs &l, const Rhs &r)
{
    return l + r;   // Error, niedozwolone w C++11
}

template<class Lhs, class Rhs>
auto add3(const Lhs &l, const Rhs &r) -> decltype(l+r)
{
    return l + r;
}

int main()
{
    std::cout << add3(3.14, 700L) << std::endl;
    return 0;
}
Wynik:
703.14

9 kwietnia 2013

Windows Sysinternals

Zbiór fajnych narzędzi administracyjnych i diagnostycznych dla system Windows - z firmay przejętej przez Microsoft, jakiś czas temu. Już dawno o tym słyszałem, ale jakoś nie miałem czasu, się im bliżej przyjrzeć. Wśród narzędzi m.in. Process Explorer, reszta osiągalna za pomocą odnośnika:
http://technet.microsoft.com/en-us/sysinternals/default

4 kwietnia 2013

python.ctypes - wołanie funkcji (shared/dynamic libs)

ctypes, to biblioteka pytahon-a umożliwiająca wołanie funkcji z dynamicznych i współdzielonych bibliotek, dla różnych platform. Przydatne linki:
By przekazać wartości przez wskaźnik/referencję można skorzystać z metody byref(), albo pointer() (ta jest jednak wolniejsza, bo tworzy prawdziwy wskaźnik). Link.
Przykład poniżej woła funkcję sscanf() (msvcrt jest Windows-ową biblioteką zawierającą większość funkcji z biblioteki standardowej C).
from ctypes import *
i = c_int()
f = c_float()
s = create_string_buffer('\000' * 32)
ctypes.cdll.msvcrt.sscanf("1 3.14 Hello", "%d %f %s", byref(i), pointer(f), s)

print i.value, f.value, s.value
I wynik:
1 3.1400001049 Hello
Chociaż interesowało mnie jeszcze kilka zagadnień (np. tworzenie struktur, tablic), nie starczyło czasu, by zrobić sobie przykłady. W razie czego, trzeba będzie wrócić do dokumentacji. A tymczasem, postanowiłem pobawić się biblioteką dźwięku BASS. Przykład poniżej, ma za zadanie przez 3 sekundy w pętli odtwarzać zawartość adios.wav.

Jeżeli mamy do czynienia z prostymi wartościami, funkcje z biblioteki możemy wołać bezpośrednio na obiekcie (bassDll).
None, integers, longs, byte strings and unicode strings are the only native Python objects that can directly be used as parameters in these function calls
import ctypes
import time

BASSVERSION      = 0x204
BASS_SAMPLE_LOOP = 4

def HIWORD(value):
    return value >> 16

bassDll = ctypes.WinDLL ("d:\\bass.dll")
if HIWORD(bassDll.BASS_GetVersion()) != BASSVERSION:
    raise Exception("An incorrect version of BASS was loaded")

if not bassDll.BASS_Init(-1, 44100, 0, 0, None):
    raise Exception("Can't initialize device")

chan = bassDll.BASS_StreamCreateFile(False, 'd:\\adios.wav', \
                                     ctypes.c_longlong(0), ctypes.c_longlong(0), \
                                     BASS_SAMPLE_LOOP)
if not chan:
    raise Exception("Can't play the file")

bassDll.BASS_ChannelPlay(chan, False)
time.sleep(3)
bassDll.BASS_Free()
print 'Success!'
W przypadku, gdy mamy do czynienia z bardziej wyrafinowanymi funkcjami, można skorzystać z WINFUNCTYPE (to dla Windows), który stworzy prototyp funkcji. Do środka przekazujemy jako pierwszy "typ zwracany", reszta to parametry jakie pobiera już właściwa funkcja. Tworząc funkcję z prototypu, przekazujemy jako drugi argument parametry właściwego wołania (linijka 13).
Pojedynczy parametr może składać się z trzech pól:
  1. Kombinacja flag określających rodzaj parametru (1 - IN, 2 - OUT, 4 - IN będący int-em ustawionym na 0 (?))
  2. Nazwa parametru (opcjonalna)
  3. Domyślna wartość dla parametru (opcjonalna)
Poniżej niezbyt wyrafinowany przykład, który wywołuje Message Box z tekstem "Hello".
import ctypes
from ctypes.wintypes import HWND, LPCSTR, UINT

# (C++)
#int WINAPI MessageBox(
#  _In_opt_  HWND hWnd,
#  _In_opt_  LPCTSTR lpText,
#  _In_opt_  LPCTSTR lpCaption,
#  _In_      UINT uType
#);

func_prototype = ctypes.WINFUNCTYPE(ctypes.c_int, HWND, LPCSTR, LPCSTR, UINT)
func_param = (1, "hwnd", 0), (1, "text", "Hello"), (1, "caption", None), (1, "flags", 0)
MessageBox = func_prototype(("MessageBoxA", ctypes.windll.user32), func_param)

MessageBox()