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()

31 marca 2013

std::initializer_list

std::initializer_list<T> to nowa struktura danych, która rozbudowuje bibliotekę standardową. Wszystkie elementy, które się w niej znajdują nie mogą być modyfikowane (coś jak const T, dla wektora). Trochę zdziwił mnie fakt, że w szablonie tym zabrakło metod cbegin/cend, zwracających const_iterator (za to są begin/end, które to robią). Z pomocą jak zwykle przyszedł stackoverflow. W tej chwili trudno mi znaleźć dla tego zastosowanie i nie wiem też, jak to jest wykorzystywane przez inne ficzery, w nowym standardzie.

Trochę więcej informacji: http://en.cppreference.com/w/cpp/utility/initializer_list

std::initializer_list<T> świetnie nadaje się inicjalizowania kontenerów (np. vector), co do tej pory było dość problematyczne. Należy jednak zwrócić uwagę, na problemy z istniejącą składnią (wsteczną kompatybilnością). vector inaczej się zachowa, gdy do konstrukcji użyjemy konstruktora z nawiasami klamerkowymi w celu wstawienia dwóch elementów, a inaczej nawiasów okrągłych (stary konstruktor, powieli N razy drugi parametr).
#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> vec1(5, 2);
    for (const auto& v : vec1)
        cout << v << " ";
    cout << endl;

    vector<int> vec2{5, 2};
    for (const auto& v : vec2)
        cout << v << " ";
    cout << endl;
}
Wynik:
2 2 2 2 2 
5 2

24 marca 2013

python -m (library module as a script)

Python posiada przydatną opcję, z którą można wykonać swoje programy (-m), czyli wykonanie modułu jako skryptu:
-m mod : run library module as a script (terminates option list)
Do nauki posłuży prosty program obliczający ciąg Fibonacciego, ale filtrujący po liczbach nieparzystych. Dodatkowo stworzyłem testy dla dwóch funkcji.
import unittest

def add(f1, f2):
    return f1 + f2

def swap(f1, f2):
    f3 = add(f1, f2)
    return f2, f3

def print_odd(num):
    print 'Odd fibo ', num

def fibo(f1 = 0, f2 = 1):
    [print_odd(num) for num in [f1, f2] if num % 2]
    for _ in xrange(20):
        f1, f2 = swap(f1, f2)
        if f2 % 2:
            print_odd(f2)

class TestSequenceFunctions(unittest.TestCase):
    def setUp(self):
        pass

    def test_add(self):
        self.assertEqual(add(2, 1), 3)

    def test_swap(self):
        self.assertEqual(swap(1, 2), (2, 3))

fibo()
Jednym z takich, przydatnych wbudowanych modułów jest profiler (przydatna instrukcja). Jak widać punkcja print_odd(), została zwołana tylko 14 razy, bo tyle jest nieparzystych liczb, wśród pierwszych 20-tu elementów ciągu.
$ python -m cProfile asdf.py

         57 function calls in 0.007 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.007    0.007 asdf.py:1(<module>)
       20    0.000    0.000    0.000    0.000 asdf.py:1(add)
        1    0.000    0.000    0.007    0.007 asdf.py:11(fibo)
       20    0.000    0.000    0.000    0.000 asdf.py:4(swap)
       14    0.007    0.001    0.007    0.001 asdf.py:8(print_odd)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
Drugi przydatny moduł, o którym się dowiedziałem służy do wykonywania testów jednostkowych.
$ python -m unittest discover --pattern=asdf.py

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

21 marca 2013

Praca z cudzym kodem

Zastanawiam się ostatnio jak ugryźć temat analizowania cudzego kodu. Nigdy nie brałem się za to zadanie formalnie, a w różny sposób zdobyta wiedza, szybko mi wyparowuje. Proces badania (reverse engineering), czasami przydaje się jedynie do zrozumienia istniejącej funkcjonalności, a czasami jest konieczny, do przeprowadzenia jakiś zmian (reengineering) np. naprawy błędów. W ostateczności, kod jest wyrocznią, tego jak działa program, nawet jak specyfikacja mówi co innego. Poniżej ciekawy link:

http://blog.smartbear.com/software-quality/bid/167035/How-to-Inherit-Somebody-Else-s-Code

I moje wnioski:
- Nie staraj się, zagłębiać w szczegóły
- Nie myśl jeszcze, o refaktoryzacji - to rozprasza
- Nie wymyślaj koła od nowa - nie myśl o przepisaniu czegoś od początku, tylko dlatego, że nie wiesz jak działa
- Analizując możesz spójrz na aplikację z punktu widzenia, końcowego użytkownika
- Bądź zorganizowany - dopasowuje istniejące moduły do opisu, który znajduje się w dokumentacji
- Najwięcej korzyści przynosi praca z kodem, który napisał ktoś mądrzejszy

9 marca 2013

gdb - ściąga

Ściąga, do pracy z gdb (choć wciąż szukam nakładki, która mogła by być trochę bardziej przyjazna użytkownikowi - coś lepszego niż ddd).