niedziela, 30 listopada 2014

Problem z operatorem trójelementowym

Parę dni temu C++ robił mi za kawę: podniósł ciśnienie bez łyka kofeiny. Poniższy kod:

#include <iostream>
#include <string>

std::ostream& operator<<(std::ostream& stream, const char* chr)
{
        stream << 0;
        return stream;
}

std::ostream& operator<<(std::ostream& stream, const std::string& str)
{
        stream << 1;
        return stream;
}

int main()
{
        std::cout << ( true ? "CCharStar" : std::string("StdString") ) << std::endl;
        std::cout << ( true ? std::string("StdString") : "CCharStar" ) << std::endl;

        return 0;
}
przeciąża operator strumienia dla łańcucha znaków oraz std::string. Zgodnie z pobieżną logiką pierwszym przypadku użycia operatora trójelementowego, z racji że warunek jest spełniony, winno wypisać "0", a w drugim "1".

To jest C++, więc nie może być łatwo. Niuans pokazany w tym kodzie był pośród setek innych linii, które posądzałem o nieprawidłowe działanie kodu. Po głębszej analizie tego, co powoduje błąd/niezrozumienie sprawa okazuje się całkiem prosta, ale serio mówiąc zajęło to trochę czasu.

W obu przypadkach zostanie wypisane "1", mimo że w pierwszym przypadku oczekiwałoby się 0. Przyczyną jest to, że operator trójpunktowy zawsze oczekuje, aby typ zwracany przez obie sekcje był identyczny, tym samym operator ?: nie jest wymienny z if-else. Tutaj są dwa różne typy, więc kompilator samowolnie szuka metody rzutowania jednego w drugi. Ponieważ std::string da się zbudować z const char*, to dochodzi do tego absurdu. Sytuacji by nie było, gdyby std::string miał konstruktor typu explicit, co oznacza niemożność niejawnego utworzenia zmiennej tego typu (np. poprzez operator trójelementowy).

Nie neguję słuszności tego mechanizmu a jedynie chcę się podzielić spostrzeżeniem, które - w bardziej 'życiowym' kodzie potrafi sprawić niemałą zagwozdkę, szczególnie w przypadku gdy programista nie jest zupełnie świadom, że oba typy użyte w tym operatorze powiązane są hierarchią dziedziczenia lub jeden da się zbudować z drugiego. Ani GCC, ani Clang (w domyślnej konfiguracji) nie wychwytują tego jako błędu. Następnym razem opiszę cyrk z tym operatorem gdy właczymy do tego dziedziczenie.

czwartek, 26 czerwca 2014

[Linux] KDE Rekonq i luka w bezpieczeństwie

Od jakiegoś czasu miałem problem z aktualizacjami systemu, w szczególności duże update'y szły jak krew z nosa. Łącze rzędu 40-50kbps przypominały mi czasy modemów ściągania map do Quake po 2 godziny jedna. Pomimo przepisania sources.list na mirror innego kraju problem nie ustępował. Dzisiaj podczas ściągania pakietu o rozmiarze < 1MB dostałem kolejne odrzucenie. Coś mnie zaintrygowało...

Od pewnego czasu próbnie używam KDE Rekonq - takiej lekkiej przeglądarki opartej o WebKit. Skonfigurowałem ją jakiś czas temu tak, aby działała przez zagraniczne proxy. Rozwiązanie? Ustawiając proxy w Rekonq ustawienie propagowane jest do konfiguracji całego KDE. Niniejszym, używając równolegle Firefox'a czy ręcznego apt-get wszystko będzie w porządku, ale np. aktualizując system za pomocą graficznego KDE Muon wszystkie aktualizacje także idą przez proxy. To samo dotyczy między innymi usług kryptograficznych (pobieranie kluczy GPG dla oprogramowania) czy pobierania paczek "security". Piękna dziura.

Aha.. teraz pobieram aktualizacje ok. 1.8Mbps.

niedziela, 23 marca 2014

Raport o zarobkach w zawodzie programisty w Polsce - Sedlak&Sedlak

Otrzymałem raport na temat wynagrodzeń na stanowisku inżynier oprogramowania w Polsce. Raport został przygotowany przez firmę Sedlak&Sedlak.

Dwie konkluzje:
  • Mityczne zarobki programistów rzędu 15 000 zł netto to bajka
  • Programiści z wyższym zarabiają mniej od tych z podyplomowym
Raport do pobrania stąd: http://pl.scribd.com/doc/213989917/Analiza-593-Inzynier-oprogramowania-pdf

Processing.js - Processing dla JavaScript

Dawno temu, w jednym z pierwszych postów hucznie zapowiedziałem język Processing. Nie zmieniam zdania, że to jeden z najbardziej innowacyjnych pomysłów ostatnich lat, bo jako jedyny umożliwia przyzwoite programowanie osobom, które nie są programistami - co więcej, pomimo że był celowany w nieprogramistów, Processing ma coraz silniejsze grono użytkowników ze strony profesjonalistów. Dlaczego? Bo nikt nie lubi się męczyć, a skoro można coś zrobić w Processing, to po co utrudniać.

Od niedawna istnieje projekt Processing.js, który umożliwia translację kodu Processing do natywnego JavaScript. Podkreślam, że nie ma miejsca żadna kompilacja. Całość pracuje natywnie w JavaScript. Oprócz oczywistej korzyści z odrzucenia konieczności eksportu do apletu Java pojawia się ogromny potencjał integracji Processing z milionem wspaniałych narzędzi: począwszy do D3JS, czy choćby jakiś MVC - w rezultacie tworzenia stron WWW z interfejsem artystycznym. Bajka!

Podtrzymuję, że JavaScript jest najważniejszą technologią tej dekady.

poniedziałek, 17 marca 2014

Google Dev Art

Dopiero dziś się o tym dowiedziałem!
Trwa Google DevArt, czyli konkurs prac wizualnych napędzanych kodem. Projekty mogą pracować pod Androidem, czy Linuxem. Dopuszczono między innymi Javę, HTML5, Cpp czy Python. Można korzystać z ogromu zewnętrznych bibliotek, także OpenCV. Co ciekawe, projekty mogą korzystać z Arduino czy Raspberry PI. Brzmi cudnie ^-^

piątek, 7 lutego 2014

Wprowadzenie do STL

Kiedyś, dawno temu, miałem okazję prowadzić w ramach koła naukowego wprowadzenie do C++ STL. Materiały szczególnie przydadzą się maturzystom wybierającym C++, studentom rozpoczynającym pracę z C++ oraz osobom, które wolą C.

Część pierwsza: http://secred.pl/2012/06/16/stl-materialy-z-prezentacji/
Część druga: http://secred.pl/2012/06/16/stl-podejscie-drugie-materialy-z-prezentacji/

wtorek, 4 lutego 2014

C/C++: Operator nawiasu kwadratowego

Niech:

int tab[] = { 1, 2, 3, 4, 5 };
const int n = 2;


wtedy można zrobić tak:

int wynik = tab[n];

ale ale ...... można też tak:

int wynik = n[tab];

Dziwne?

W zasadzie tak wyszło przez przypadek. Gdy powstawał C (a może nawet B), operator nawiasu kwadratowego był lukrem składniowym. Nim wprowadzono go do języka n-ty element tablicy tab otrzymywano w taki sposób:

T operator[]( loperand, roperand )
{
  return *(loperand+roperand);
}
(co całkiem nieźle tłumaczy czemu tablice numerujemy od zera)

Później wprowadzono operator[], który wewnątrz robi dokładnie to samo, co kod powyżej. Ponieważ relacja jest przemienna, bo *(tab+n) <=> *(n+tab), to możliwe jest zamiana indeksu i tablicy.

Lubię ten język : )

piątek, 31 stycznia 2014

Dziwny błąd, jeszcze dziwniejsze rozwiązanie

Przypadkiem dowiedziałem się, że brak przecinka między dwoma łańcuchami jest poprawnym wyrażeniem C. Co zabawniejsze, oznacza konkatenację.

Weźmy taki przykład:

char* tab[] = { "Ala", "ma", "kota", "imieniem" "Filemon" };


zatem tab[4] powoduje segfault, ale program się całkiem zacnie skompiluje.

Zapominając o przecinku można się tyyyle nauczyć ;)
Edit: W GCC nawet dołączenie -ansi -pedantic -Wall nie powoduje pojawienia się ostrzeżenia.

niedziela, 26 stycznia 2014

C++: Mały błąd, duży problem

Strasznie dawno mnie tu nie było ...

Dziś do szybkiego przemyślenia coś, co potrafi wprawić w osłupienie gdy niewinne parę linijek kodu blokuje działanie dużego modułu, który nie ma nic wspólnego z matematyką.

EDIT Po poście Sebastiana uprościłem kod do absolutnego minimum, aby uwidocznić problem
Oto i on!
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cmath>
 
unsigned long long int suma = 0;
 
int main()
{
 srand( time ( NULL ) );

 unsigned long long int losowa = rand() << 10 | rand() << 5 | rand();

 for ( int i = 63; i >= 0; i-- )
 {
  if ( ( (1<<i) & losowa ) > 0  )
  {
   suma += ( 1<<i );
  }
 }
 std::cout << "Suma: " << suma << std::endl;
 std::cout << "Losowa: " << losowa << std::endl;
 return 0;
}

niedziela, 21 lipca 2013

Proste filtry graficzne w Processing : zrozumieć zapis koloru

Strasznie mi wstyd, że od miesiąca nie znalazłem czasu, by tu zajrzeć.
Dziś wrócę do moich korzeni, czyli styku programowania i grafiki komputerowej. Chciałbym pokazać osobom mniej zaawansowanym jak napisać prosty filtr do obrazu statycznego. Będę pracował nad zdjęciem Cateriny, której fotkę można znaleźć tu: http://commons.wikimedia.org/wiki/File:Luca_Patrone_Caterina_in_autumn.jpg Obraz pochodzi z Wikicommons, czyli możemy z niego legalnie i za darmo korzystać; objęty jest licencją CC BY-SA (tutaj możesz poczytać o czym ona mówi). Obraz został znormalizowany i skadrowany.

Filtr, który będę pokazywał nazywa się plamą barwną. Obraz po zadziałaniu filtra winien wyglądać, jakby był malowany farbami. Znajdujące się obok siebie piksele powinny zlać się ze sobą dając efekt plamy, która powstanie poprzez zatarcie drobnych różnic kolorystycznych pomiędzy pikselami. Nie będziemy rozmazywali obrazu.

Zanim jednak, muszę przypomnieć dwie tożsamości z logiki Boole'a. Z tabeli prawdy koniunkcji wynika, że:

W językach C, C++ czy Java (Processing jest nakładką na Java) istnieją dwa operatory koniunkcji. Pojedynczy ampersand (&) oraz podwójny (&&). Użycie podwójnego ampersanda bierze lewe i prawe wyrażenie, i pomiędzy tymi dwoma wyrażeniami oblicza wartość logiczną. Nas bardziej interesuje operator pojedynczego ampersanda. Oba operandy pojedynczego ampersanda (liczby, nie wartości logiczne) są traktowane operatorem koniunkcji w taki sposób, że pierwszy bit pierwszego operandu jest w koniunkcji z pierwszym bitem drugiego operandu, drugi bit pierwszego operandu z drugim bitem drugiego operandu itd.




Na rysunku powyżej pokazałem przykład działania operatora ampersand. Z tożsamości u góry wynika, że te bity drugiego operandu na których są jedynki spowodują przepisanie bitów pierwszego operandu, a tam gdzie drugi operand ma zera, w wyniku znajdą się zera. Aby było szybciej, operację koniunkcji bitowej będę nazywał and-owaniem.

Zakładam, że obraz zapisany jest za pomocą przestrzeni koloru RGB, 24 bity / piksel, w formacie RGBRGBRGB... Załóżmy, że obraz jest w skali szarości, czyli R=G=B. Na ostatnim bicie (prawym, LSB) można zapisać wartość co najwyżej wartość 1, na skrajnie lewym (MSB) co najwyżej 127.

Jesteśmy w skali szarości. Wykonując koniunkcję piksela przez wartość 255 jego wartość się nie zmieni, natomiast jeżeli dwa piksele różnią się minimalnie (o jeden), to gdyby wykonać koniunkcję przez 0b11111110, czyli 254, to różnice między tymi pikselami by się zatarły. Bazując na tej zasadzie będzie wykonywany efekt plamy barwnej. Wpierw na obrazie w skali szarości (znormalizowanym), później kolorowym. Caterina będzie miała ciężki dzień.

Przygotowałem następujący zestaw masek przez które będę and-ował kolejne piksele obrazka:




odpowiadające kolejno wartościom: 0xFF, 0xF8, 0xF0, 0xE0, 0xC0, 0x80. Ponieważ każda z tych masek dotyczy tylko jednej składowej koloru, to w praktyce każdy piksel trzeba będzie and-ować przez 0xFFFFFF, 0xF8F8F8 itd...

Wraz z kolejną maską zacierane będzie coraz więcej informacji o kolorze, co oznacza że coraz liczniejsze grupy pikseli będą miały identyczną wartość.

Uruchomiłem kod generujący maskowanie pikseli zgodnie z opisanym algorytmem. Wyszło tak:


Lewy górny róg obrazu opisuje wartość hex maski użytej do uzyskania obrazu.

Na obrazie kolorowym można spodziewać się efektów dodatkowych. Wartości RGB będą różne, zatem możliwe że dla danego koloru przeand-owanie piksela spowoduje że nie wszystkie składowe koloru ulegną zmianie, a w rezultacie że na obrazie pojawią się kolory, których nigdy nie było. Przykład:


 
Oba kolory po prawej powstały przez przeand-owanie koloru po lewej, lecz tak jak w pierwszym wypadku z zieleni przeszło w zieleń, tak w drugim pojawił się niepodobny kolor. Wracając, na obrazie kolorowym wyszło tak:


Kod generujący powyższe filtr w języku Processing wygląda następująco:


/*
 * Prosty filtr graficzny - efekt barwnej plamy w Processing
 * Copyright (C) 2013  Adam 'foo-script' Rakowski
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.

 * Źrodło obrazka z przykładu: http://commons.wikimedia.org/wiki/File:Luca_Patrone_Caterina_in_autumn.jpg
 * Obraz na licencji CC BY-SA
 */


PImage przed;

void setup()
{
  przed = loadImage("obraz.jpg");
  if (przed==null)
  {
    print("Obraz nie istnieje");
    return;
  }
  
  size( przed.width*2, przed.height*3);
  
  int[] maski = { 0xf8f8f8, 0xf0f0f0, 0xe0e0e0, 0xc0c0c0, 0x808080 };

  image( przed, 0, 0 );
  for (int w=0; w<3; w++)
    for (int k=0; k<2; k++)
    {
      if (w==0 && k==0) continue;
      image( filtr_plamy(przed, maski[2*w+k-1]) , k*przed.width, w*przed.height);
      text( String.format("%H", maski[2*w+k-1]) , k*przed.width, w*przed.height+10 );
    }
}

PImage filtr_plamy(PImage zrodlo, int maska)
{
  assert(zrodlo != null);
  PImage wynik=przed.get();
  wynik.loadPixels();
  for (int i=0; i<zrodlo.width * zrodlo.height; i++)
    wynik.pixels[i] &= maska;
  return wynik;
}