wtorek, 20 grudnia 2011

Używanie słówka kluczowego inline zamiast makr w C



Dobry kod zawiera zwykle dużą liczbę relatywnie małych funkcji czy metod, które mogą być łączone ze sobą na wiele sposobów (jak klocki Lego). Często jednak pisząc kod tworzymy mniej, bardziej złożonych i mniej uniwersalnych funkcji. Sprawia to, że kod jest bardziej podatny na błędy i często sprawia problemy przy testowaniu czy szukaniu błędów.

Często powodem takiego stanu rzeczy jest nieużywanie małych funkcji aby nie obniżać wydajności systemu. Ciągłe wywoływanie  małej funkcji może znacznie zmniejszyć prędkość wykonywania całego programu. Oczywiście można zainwestować w szybszy procesor ale nie zawsze jest to możliwe do wykonania. Może nam zależeć na minimalizacji czasu wykonania jakiejś funkcji.

Rozważmy taki przykład. Mamy funkcję, która dodaje 1 do jakiejś wartości ale z tzw. nasyceniem aby nie przekroczyć maksymalnej wartości (żeby zmienna się nie "przekręciła"):

  int SaturatingIncrement(int x)
  { if (x != MAXINT)
    { x++;
    }
    return(x);
  }

Możemy mieć więc kod wyglądający jak poniżej:
  ...
  x = SaturatingIncrement(x);
  ...
  z = SaturatingIncrement(z);

Na pewno zauważysz, że jeśli wielokrotnie funkcja ta będzie wykonywana to kod będzie wykonywał się wolno. Zwykle rozwiązania są 2. Niektórzy, kopiują kod z funkcji i używają go mniej więcej tak:
  ...
  if (x != MAXINT)  { x++; }
  ...
  if (z != MAXINT)  { z++; }

Dużym problemem z tym jest to, że jeśli znajdziesz błąd, musisz przejrzeć cały program i poszukać wszystkich wystąpień takich fragmentów kodu i ręcznie je poprawić.

Trochę lepszym rozwiązaniem jest użycie makr:
#define SaturatingIncrement(w)  { if ((w) != MAXINT)  { (w)++; } }

które, pozwala wrócić mniej więcej do oryginalnego kodu. Kod więc może wówczas wyglądać tak:
  ...
  SaturatingIncrement(x);
  ...
  SaturatingIncrement(z);

ale preprocesor wykryje użycie makra i zamieni to na taki kod dla kompilatora:
  ...
  if (x != MAXINT)  { x++; }
  ...
  if (z != MAXINT)  { z++; }

eliminując tym samym kilka niepotrzebnych taktów przy wywołaniu funkcji.

Fajną rzeczą w makrach jest to, że jeśli znajdziesz błąd to musisz poprawić go tylko w jednym miejscu i sprawia, że kod jest dużo bardziej czytelny. Tak czy inaczej złożone makra mogą być niewygodne i być źródłem tajemniczych błędów (np. wiesz dlaczego jest "(w)" zamiast po prostu w?).

Dobrą wiadomością jest to, że we większości kompilatorów C jest lepsze rozwiązanie. Zamiast używać makr można użyć funkcji ze słówkiem kluczowym "inline".

  inline int SaturatingIncrement(int x)
  { if (x != MAXINT)
    { x++; }
    return(x);
  }

Słówko inline mówi kompilatorowi aby rozwinął kod funkcji w linii gdzie wywołana jest funkcja (tak jak makro). Ale zamiast zamiany tekstu w preprocesorze jest do dokonywane przez sam kompilator. Można więc pisać tyle funkcji inline ile się chce bez płacenie niepotrzebnymi taktami zegara. Dodatkowo, kompilator sprawdzi poprawność typów czy inne analizy aby pomóc wykryć błędy co nie było możliwe z makrami.

Można się jednak spotkać z kilkoma dziwactwami związanymi z inline. Niektóre kompilatory ignorują słówko inline jeśli funkcja przekracza jakąś określoną liczbę linii kodu. Inne z kolei pozwalają wywołać w ten sposób funkcje która jest zdefiniowana w tym samym pliku .c. Niektóre kompilatory muszą mieć zaznaczoną flagę aby wymusić inline zamiast zwykłego słówka inline przy definicji funkcji. Niestety aby być pewnym, że inline działa poprawnie, trzeba sprawdzić kod wyjściowy (w asemblerze) i zobaczyć jak nasz kompilator to obsługuje.

sobota, 10 grudnia 2011

Nowe zastosowania dla rejestrów RTC

Chcę dzisiaj pokazać nowe zastosowanie rejestrów zegara czasu rzeczywistego, które są dostępne w niektórych (wielu?) procesorach ARM jak również jak podejrzewam we wielu innych architekturach. Fragment z dokumentacji procesora firmy NXP przedstawiłem na rysunku poniżej.
Rejestry RTC procesorów z rodziny LPC17xx
Najbardziej interesująca jest kolumna Reset Value. Można zauważyć, że wartości zaznaczone na czerwono mają wartość "NC". "NC" oznacza, że rejestry te są nienaruszone podczas resetu procesora. Ponadto, rejestry te mogą być również zasilane z alternatywnego źródła zasilania, więc są one również odporne na utratę zasilania. Do czego to się przydaj? Otóż znalazłem kilka zastosowań dla nich poza oczywistym przeznaczeniem jakim jest przechowywanie daty i czasu (dla rejestrów RTC) i zapewnienia nieulotnej pamięci przez rejestów ogólnego przeznaczenia (General Purpose Registers).

Komunikacja z Bootloaderem
Większość dzisiejszych aplikacji wbudowanych zawiera program ładujący (bootloader). Chociaż istnieje kilka sposobów uruchamiana bootloadera, to najpopularniejszym i dostępnym we większości mikrokontrolerów sposobem jest wymuszenie resetu przez watchdog. Jednak sam reset nie wystarczy. Wymagana jest zwykle jakaś komunikacja między główna aplikacją a aplikacją ładującą. Bootloader musi co najmniej wiedzieć, że został celowo uruchomiony w celu wykonania aktualizacji oprogramowania a nie zostało to spowodowane przez błąd w programie i nieprzewidziany reset przez watchdoga. Może również się zdarzyć, że wymagana będzie informacja o tym z jakiego źródła ma być pobrany nowy program. W przeszłości istniała tendencja do przekazywania tych informacji poprzez pamięć EEPROM. Teraz mając powyższe rejestry można je wykorzystać do tego celu.

Debugowanie
Jeśli borykasz się z niespodziewanymi resetami systemu, to jest to stosunkowo prostą sprawą, aby napisać kilka linii kodu, który zapisuje w tych rejestrach informacje o kontekście. Na przykład większość RTOSów zapewnia mechanizmy wywołania funkcji użytkownika przez przełączeniem zadania. W funkcji tej banalne jest zapisanie do jednego z rejestrów ID zadania. Wtedy to tylko kwestia ustawienia breakpointa na wejściu do funkcji main() i odczyt które zadanie było uruchomione w momencie resetu. Zawężenie podejrzanych bardzo ułatwia sprawę.

Podejrzewał, że można znaleźć jeszcze wile zastosowań tych rejestrów. Na prawdę chciałbym aby rejestry tego typu były powszechne i dostępne w każdej rodzinie procesorów.

piątek, 25 listopada 2011

Szansa na darmowe Rasberry Pi od Nokii

Na blogu Nokia Qt pojawiła się informacja o tym, że Nokia sponsoruje 400 Raspberry Pi dla developerów. Szczegóły na: http://blog.qt.nokia.com/2011/11/24/qt-5-with-rasberry-pi-a-delicious-mix-with-home-delivery/

Zgłosiłem już własny pomysł - może się uda. Jak nie to trudno i tak sobie kupię układzik, $25 to w sumie nie wiele jak za takie coś :)

Play - promocja świąteczna (dla naiwnych)

Rzecznik Play umieścił na swoim blogu informacje o promocji świątecznej. Po przeczytaniu tej wiadomości nawet się ucieszyłem wysyłam SMSa i dostaję 50zł na rozmowy i SMS/MMSy. Jedynym warunkiem było to aby na koncie znajdowało się 30zł.
Niestety uśmiech z twarzy znikł mi gdy pobrałem regulamin, z którego wynika że nie dostajemy 50zł a kupujemy sobie pakiet 50zł za 30zł :(
Aktywna jest w tej chwili również inna promocja w ramach której za 10zł możemy kupić pakiet 30zł.
Nawet dziecko zauważyłoby, że to jakiś przekręt...

poniedziałek, 21 listopada 2011

C# - asynchroniczne pobieranie pliku

Aby pobrać plik z internetu nie blokując przy tym głównego wątku można użyć metody DownloadFileAsync z klasy WebClient. Można również dodać obsługę eventów aby wyświetlać postęp i wykryć kiedy plik został pobrany w całości. Przykładowy kod poniżej.

private void button1_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
WebClient wc = new WebClient();
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
wc.DownloadFileAsync(new Uri(textBox1.Text), sfd.FileName);
}
}


void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}


void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
MessageBox.Show("Pobieranie ukończone");
}


Gotowy projekt do pobrania: DownloadAsync.zip


I na koniec jeszcze jedna uwaga. Chociaż metoda jest asynchroniczna to i tak może na chwilę zablokować główny wątek. Dzieje się tak ponieważ przed pobraniem pliku konieczna jest zamiana adresu na IP i dzieje się to wewnątrz funkcji, która niestety jest blokująca. Aby temu zapobiec wystarczy (o ile to możliwe) zamiast nazwy podać IP.

czwartek, 27 października 2011

Google+ dla użytkowników Google Apps

W końcu można korzystać z Google+ w Google Apps. Swoją drogą dziwne posunięcie ze strony Google, że nowe usługi są udostępniane dla tych użytkowników (moim zdaniem najwierniejszych, bo część usług w swojej domenie powierzają właśnie im), są udostępniane tak późno.
Niestety aby Google+ było dostępne we własnej domenie trzeba je najpierw ręcznie włączyć (szczegóły na http://www.google.com/support/a/bin/answer.py?answer=182442&hl=pl). Odkryłem to w sumie przez przypadek. Szkoda, że nie są wysyłane e-maile do użytkowników z tą informacją (przynajmniej ja nie dostałem).

wtorek, 4 października 2011

Aero2 - pierwszy test

Dziś listonosz dostarczył kartę więc czas na pierwsze testy. Nie zdążyłem się jeszcze zaopatrzyć w modem na USB więc posłużyłem się moją Nokią 5230.
Włożyłem kartę do telefonu jednak nie zalogował się automatycznie. Postanowiłem więc ręcznie wybrać sieć. Na liście dostępnych pojawiła się sieć Aero2 ale po jej wybraniu telefon po dłuższej chwili odpyskował tylko komunikatem: "Brak dostępu".
Postanowiłem więc podejść trochę inaczej do tematu. Uruchomiłem na komputerze Nokia Ovi Suite skonfigurowałem połączenie internetowe (APN: darmowy) i spróbowałem się połączyć. O dziwo połączył się bez problemów.
No to przyszła pora na testy prędkości (http://www.speedtest.net/) Przetestowałem kilka serwerów i wyszło mniej więcej tak:

  • Pingi ok 300ms
  • Download 0,25Mb/s
  • Upload 0,05Mb/s


Bez rewelacji ale najważniejsze, że działa - przyda się w kryzysowych sytuacjach.

czwartek, 29 września 2011

Aero2 - darmowy internet ciąg dalszy

Dziś dostałem e-maila z informacją, że karta została dziś wysłana. Widać szybciej im to idzie niż planowali. Zgodnie z informacją na stronie powinni wysłać ok 10 października, więc pozytywna niespodzianka.

Trzeba się teraz rozejrzeć za jakimś modemem na USB.

środa, 14 września 2011

C# - parsowanie double z kropką i przecinkiem

Różne programy różnie zapisują liczbę zmiennoprzecinkową. Jedne używają kropki w celu oddzielenia części ułamkowej od części całkowitej. Na dodatek ten sam program może różnie zapisywać w zależności od ustawień systemu w którym jest uruchomiony.
Napisałem więc prostą funkcję, która próbuje skonwertować stringa do double:

private static double GetDouble(string value)
{
    double res;
    if (!double.TryParse(value,NumberStyles.Any, CultureInfo.CurrentCulture, out res))
    {
        if (!double.TryParse(value, NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out res))
        {
            if (!double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out res))
            {
                res = double.NaN;
            }
        }
    }
    return res;
}

poniedziałek, 12 września 2011

Testuj smartfona - wrażenia po testach

Skończył się czas testowania smartfona w Play. Jakie wrażenia? Krótko mówiąc mieszane. Ogólnie fajna sprawa dostać telefon z darmowym netem. Tak się złożyło, że ostatnio dużo podróżuję i bezprzewodowy internet bardzo się przydaje. Szczególnie przypadła mi do gustu funkcja access pointa.
Niestety Play nie postarał się wybierając telefon. Stary, wolny jednordzeniowy procesor, mało RAMu i telefon nie nadaję się do użytku. Często zastanawiałem się czy kliknąłem w ikonkę czy nie, czy aplikacja jeszcze się uruchamia itp. Następny minus to bateria czy też prądożerność telefonu. Jeśli włączyło się połączenie 3G to bateria nie wytrzymywała nawet pół dnia. Żebym to jeszcze jakoś intensywnie go eksploatował ale nie, samo sprawdzanie skrzynki mailowej w tle skutecznie rozładowywało komórkę. Rozwiązaniem było wyłączanie połączenia po skorzystaniu z telefonu (o minusach tego rozwiązania chyba nie muszę pisać) i korzystanie gdzie się dało z wifi. W ten sposób można było osiągnąć rezultat 1.5 do 2 dni.
Podsumowując akcja fajna, co niektórzy pewnie się przekonali, ale można było trochę bardziej to przygotować, wydać trochę więcej pieniędzy na smartfony a w ostateczności rozciągnąć w czasie akcję aby przy użyciu mniejszej liczby telefonów można było nadal przekazać do testowania tej samej liczbie osób.

niedziela, 11 września 2011

Darmowy internet od Aero2 - zmiany w wydawaniu kart

Pewnie niejeden słyszał już o darmowym bezprzewodowym internecie od Aero2. Dotychczas polegało to na tym, że należało wypełnić formularz, wpłacić depozyt w wysokości 50zł i najgorszy ze wszystkiego (przynajmniej dla mnie, mieszkańca Gdańska) odbiór osobisty karty SIM w biurze firmy w Warszawie.
Na szczęście po interwencji UKE można zamówić kartę i zażyczyć sobie wysyłkę do domu za 7zł. Przy okazji zmalała wartość depozytu do 20zł.
Jako że koszty się zmniejszyły i mogę wszystko zrobić bez ruszania się z domu (no po za spacerem na pocztę) postanowiłem spróbować. Wpłaciłem pieniądze (2 przelewy na dwa różne konta) i czekam na odpowiedź. W dniu wysyłki dokumentów (6 września)  brak było już kart ale dostawa miała być 9. września.
Dziś sprawdziłem czy są jakieś nowe wieści na stronie. Pojawił się harmonogram wysyłki kart. Trzeba będzie poczekać do 10 października więc cierpliwie czekam. Jak coś się ruszy na pewno dam znać :)