Skip to: Site menu | Main content

Autor

Chłopak z Bałut (Dołów), po uniwerku i stypendium. Wiecznie zestresowane, przemądrzałe bezguście. Więcej na stronie domowej.

Twarde linki a edytory

Klepanie, Techblog

Ku pamięci: edytory tekstu zaburzają twarde linki. Edycja odbywa się bowiem na kopii pliku, a oryginał zostaje nietknięty. Przy zapisie, edytory odlinkowują oryginał (bądź zmieniają mu nazwę na plik kopii zapasowej) i zmieniają nazwę pliku tymczasowego. Na szczęście da się tego uniknąć. Aby zapis odbywał się do oryginalnego pliku, należy dodać następujące wiersze do .emacs:

    (setq backup-by-copying nil)
    (setq backup-by-copying-when-linked t)

Dla ewentualnych współpracowników korzystających z vi rozwiązaniem jest dodanie następującego wiersza do konfiguracji:

    set bkc=yes

13 września 2009, 14:38:07 3 komentarze

Adresy e-mail web developerów z Polski

Czepianie, Klepanie, Techblog

Dzisiaj, w sposób zupełnie przypadkowy, znalazłem sposób na sporządzenie listy adresów e-mail web developerów z Polski. Oczywiście nie wszystkich. Ale w ciągu kilkunastu minut udało mi się ich znaleźć ponad 500. Zanim pokażę, jak to zrobić, krótka dygresja.

Google, jak wiadomo, bardzo dba o to, żeby adresy e-mail użytkowników ich serwisów nie dawały się zbyt łatwo wydobyć przy pomocy zautomatyzowanych harvesterów. I tak na przykład w wynikach wyszukiwania Google Groups adresy e-mail pojawiają się zapisane w taki oto sposób:
Google Group Search
Jakby tego było mało, adresy są również ukrywane w treści wiadomości:
Google Groups e-mail obfuscation

I to właśnie wydaje mi się fascynujące - jak wiele wysiłku można włożyć w zabezpieczenia, żeby potem inna grupa pracowników firmy wydała adresy e-mail twórców gadgetów Google... ot tak: Google Gadget search

Około piętnastu minut zajęło mi przygotowanie skryptu, który przy pomocy wgeta (tu trzeba pamiętać o ustawieniu User-Agent), grepa, seda, pythona (urllib) i tr'a (strona wyników nie zawiera znaków nowego wiersza) wybiera listę 550 adresów e-mail. A to próbka, na dowód:
adresy e-mail

Mój adres również się wśród nich znajduje - tak właśnie na to trafiłem. Ciekaw jestem, czy kreatywny pracownik firmy, który wpadł na "genialny" pomysł dodania zlokalizowanych rekordów do bazy adresowej Google Maps, zgarnął za to premię :)

17 maja 2009, 13:00:38 7 komentarzy

Zagadka

Klepanie

Poniższy zrzut ekranu prezentuje fragment kodu źródłowego w Javie. Tak, ten kod nie tylko się kompiluje, ale nawet działa i sortuje, o czym świadczą wyniki na konsoli. To jest autentyczny screenshot z zupełnie normalnego Eclipse, z normalną perspektywą Javy. Ani grama Photoshopa.
Eclipse
Potrafi ktoś wytłumaczyć ten fenomen?

22 lipca 2007, 13:55:01 7 komentarzy

Libtrash a SQLite

Klepanie, Techblog

Ostatnio, przy okazji analizowania danych z GoldenLine, miałem olbrzymi problem z wydajnością bazy danych SQLite. Obszedłem go w ten sposób, że zrestartowałem skrypt i zacząłem zapisywać dane do nowej bazy, ale męczyło mnie to nadal i dzisiaj postanowiłem zdiagnozować sytuację. Okazało się, że wina leży po stronie biblioteki Libtrash.

Libtrash to uniwersalny kosz dla systemów GNU. Wystarczy przy uruchomieniu wyeksportować zmienną LD_PRELOAD, do której przypisze się ścieżkę do biblioteki i wszystkie wywołania unlink zostaną przechwycone, a plik przeniesiony do kosza (bibliotekę można tak skonfigurować, żeby mimo wszystko usuwała określone rodzaje plików). Mechanizm jest o tyle ciekawy, że działa zawsze - bez względu na program, którego używamy.

No i tu pojawił się problem. SQLite, przy każdym wywołaniu, otwierał nową transakcję i tworzył plik dziennika, który następnie usuwał. Libtrash postępuje w ten sposób, że w przypadku usunięcia pliku, który już istnieje w koszu, nadaje kopiom kolejne numery. Niestety, korzystał przy tym z liniowego algorytmu, który, po usunięciu ok. 50000 dzienników, zaczął strasznie spowalniać skrypt. Oczywiście, pliki dziennika można wyłączyć spod ochrony libtrasha. Mimo wszystko, postanowiłem usprawnić bibliotekę, zamieniając liniowy algorytm na logarytmiczny. Z mojej strony można pobrać patch do wersji 2.6, który za jakiś czas być może pojawi się również w kolejnej wersji tej biblioteki.

01 maja 2007, 17:34:16 Dodaj komentarz

Ostatni wpis o open-tran

Klepanie

Pracowity weekend zaliczyłem. Open-Tran ma od dziś najnowsze tłumaczenia KDE na 83 języki. Niestety nie udało mi się zaimportować kaszubskiego (a także gruzińskiego, telugu i hausa), ale mam nadzieję nadrobić to w najbliższym czasie. A wpis ten jest ostatni, bo dalsze informacje na temat projektu publikować będę na nowym blogu open-tran.eu.

11 marca 2007, 22:37:19 Dodaj komentarz

Zmiany na open-tran.eu

Klepanie, Techblog

W ramach realizacji pierwszych feature requestów dodałem formularz na stronie głównej wyszukiwania. Ale najważniejsze zmieniło się pod spodem. W tej chwili open-tran.eu jest gotowy do przyjęcia innych wersji językowych. Jak tylko będę miał czas, to wrzucę obsługę kilku najpopularniejszych języków i poszerzę bazę o tłumaczenia Gnome'a. Tylko co tu zrobić, żeby serwis wypromować?

22 lutego 2007, 22:14:03 5 komentarzy

Open-Tran.eu

Klepanie, Narcyzm

Wczoraj uruchomiłem serwis dla tłumaczy oprogramowania open source open-tran.eu. W tej chwili serwis potrafi sugerować tłumaczenia angielskich fraz na język polski bazując na tym jak zostały one (lub podobne frazy) przetłumaczone w KDE.

Zapraszam oczywiście na stronę, żeby zobaczyć jak to działa i o co w ogóle chodzi. A chętnych do współpracy gorąco zachęcam do przesyłania mi wszelkich uwag na mejla (np. sliwers@googlemail.com) albo jida (sliwers@jabberpl.org).

14 lutego 2007, 21:38:17 4 komentarze

Jawne implementowanie interfejsów

Czepianie, Klepanie

Znowu mam powód do ponarzekania. I znowu dzięki panom dizajnerom z Microsoftu, którzy zrobili wszystko, żeby utrudnić życie programistom. A wszystko za sprawą cudownego mechanizmu zwanego explicit implementation. Generalnie rzecz biorąc idea może nie jest taka najgłupsza, ale dlaczego ja się pytam, tak dużo uwagi przyłożono do tego, aby utrudnić ludziom życie?

Historia zaczyna się tak, że tworzymy sobie pakiet rozszerzający VisualStudio. Robi się to w ten miły sposób, że dziedziczymy z odpowiedniej abstrakcyjnej klasy Package, dekorujemy ją jakąś setką atrybutów (z czego połowa to GUIDy, jedna czwarta to numery kluczy do resource'ów, a reszta to jakieś inne stringi) i implementujemy sobie jakieś tam metody. Do tego przychodzi nam na myśl, że fajnie byłoby zaimplementować interfejs IOleCośTam, który już jest implementowany przez klasę Package. Jest to o tyle fajna sytuacja, że w większości przypadków moglibyśmy się zdać na Package i obsługiwać tylko te, które nas interesują. I tu w naszą drogę wchodzi właśnie jawna implementacja interfejsów. Dla uproszczenia przyjmijmy taką sytuację:

    public interface IFoo
    {
        void Foo();
    }
    public class Bar : IFoo
    {
        void IFoo.Foo() { ... }
    }

Tak więc mamy sobie fajną klasę Bar. Wydziedziczymy z niej sobie naszą nową klasę, w której spróbujemy przeciążyć metodę Foo, ale w taki sposób, żeby wywołać Foo z klasy Bar.

    public class Baaz : Bar, IFoo
    {
        public Foo()
        {
            base.Foo();
        }
    }

Bad luck! Metoda Foo nie została w klasie Bar bez powodu oznaczona jako publiczna, żebyśmy ją sobie teraz ot tak po prostu mogli wywołać.... no dobra, to może tak:

    public class Baaz : Bar, IFoo
    {
        public Foo()
        {
            ((IFoo)base).Foo();
        }
    }

I to jest właśnie to miejsce, w którym panowie z Microsoftu na chama stawiają szlaban. Otóż kompilator mówi, że wksaźnika base nie można sobie przerzutować na coś innego. Oczywiście przerzutowanie this na odpowiedni interfejs kompiluje się poprawnie i w sposób jak najbardziej poprawny prowadzi do stack overflow poprzez nieskończoną rekurencję. Więc jak? Otóż tak:

    public class Baaz : Bar, IFoo
    {
        public Foo()
        {
            Type t = typeof(Bar);
            InterfaceMapping map = t.GetInterfaceMap(typeof(IFoo));
            MethodInfo mi = Array.Find(map.TargetMethods, 
                    delegate(MethodInfo m) { return m.Name.Equals("Bar.Foo"); });
            
            mi.Invoke(this, null);
        }
    }

Prawda, że elegancko? Dobieramy się do metody klasy bazowej przez reflection (trzeba tutaj zwrócić uwagę na podanie nazwy metody poprzedzonej kwalifikowaną nazwą klasy), po czym wywołujemy ją przy pomocy Invoke. Oczywiście możemy zapomnieć o kontroli typów.

I ja się teraz pytam. Skoro do tej metody i tak można się dostać, skoro istnieją takie sytuacje, w których to jest konieczne, to dlaczego, dlaczego, dlaczego musiało to zostać tak cholernie utrudnione? Dlaczego nie można było dać ludziom do ręki normalnego mechanizmu dostępu do tych metod?

Panów dizajnerów C# radzę wysłać na tygodniowy kurs Pythona. Może przy następnej próbie utrudniania życia programistom ręka im zadrży.

29 stycznia 2007, 21:20:22 1 komentarz

Dalsze moich z Pythonem przygody

Klepanie, Narcyzm

Parę miesięcy temu napisałem sobie skrypcik w pythonie, który wyświetlał mi status połączenia z siecią (sieć mam niestety meteopatyczną - jak pada, to się gorzej czuje). Potem przesiadłem się na Gnome'a, a ten ma taki aplecik do wyświetlania statusu, więc skrypt poszedł w niepamięć. Ale niedawno padł nam jeden z monitorów i z konieczności siedzę przy laptopie, podłączonym kablem usb-usb z komputerem stacjonarnym. Nie dość, że aplecik nie wyświetla stanu połączenia komputera stacjonarnego, to na dodatek sam kabel się czasami zacina. Otworzyłem więc sobie wczoraj mój stary skrypcik i przerobiłem go na klienta i serwera.

A czemu się chwalę? Bo nigdy w życiu nie przyszłoby mi to tak szybko, gdybym nie znał C. Python może ma inną składnię, ale biblioteka standardowa działa na dokładnie tej samej zasadzie. A przy okazji po raz pierwszy przydał mi się góglowy code search, który przypomniał mi jaki się przekazuje level do setsockopta.

15 listopada 2006, 20:20:49 Dodaj komentarz

Jak się ma AssemblyBuilderAccess do CreateType?

Klepanie

Kiedyś wyczytałem w bardzo fajnym poradniku, że tak naprawdę istnieją tylko dwie flagi AssemblyBuilderAccess. Jedna służy do tworzenia tymczasowych modułów uruchamialnych (Run), a druga do tworzenia takich, które są zachowywane do pliku (Save). Dzisiaj boleśnie przekonałem się o tym, że to jednak nie jest tak. Trzecia flaga (RunAndSave) jest również istotna. Jeśli jeden typ zapisujesz do jednej deelelki, a drugi typ (który odwołuje się do tego pierwszego) chcesz zapisać do drugiej deelelki (która naturalnie musi referencjonować tę pierwszą) i dostajesz wyjątek z komunikatem "Could not load file or assembly" przy wywołaniu CreateType na odpowiednim TypeBuilderze, to spróbuj zamienić flagę Save na RunAndSave. Mnie tak zadziałało.

09 listopada 2006, 10:00:28 Dodaj komentarz

Jeszcze więcej problemów z Reflection.Emit

Czepianie, Klepanie

Jedną mam olbrzymią radę dla wszystkich, którym by kiedykolwiek do głowy przyszło korzystać z niekonsekwentnie zaprojektowanego, słabo udokumentowanego i kiepsko zaimplementowanego nejmspejsa: zastanów się dwa razy, zanim wpakujesz się w to po uszy.

Dzisiejsze godziny poranne upłynęły mi pod znakiem próby dobrania się do konstruktora listy o elementach typu, który jest w trakcie generowania. Konkretnie chodzi o takie coś:

    ConstructorInfo GetListConstructorInfo(TypeBuilder elementType)
    {
        Type listType = typeof(List<>).MakeGenericType(elementType);
        return listType.GetConstructor(Type.EmptyTypes);
    }

Specjalnie zadeklarowałem tu elementType jako TypeBuilder, żeby podkreślić, że ten typ nie został jeszcze stworzony. Jeśli ktoś śledził moje dotychczasowe przygody, to już wie czym się kończy wykonanie tego kodu: NotSupportedException.

24 października 2006, 20:43:25 Dodaj komentarz

Niekonsekwencja

Czepianie, Klepanie

Znowu będzie o Reflection. Najpierw odpowiem sobie sam jak ładnie stworzyć sobie obiekt reprezentujący typ polimorficzny:

    return typeof(List<>).MakeGenericType(new Type[] { elementType });

Nie przypuszczałem, że C# dopuszcza coś takiego jak List<>. Niestety okazuje się, że nie wszędzie się programistom Microsoftu chciało i poniższy fragment kodu już przez parser nie przejdzie.

    class A<T> { ... }
    class B<S> where S : A<> { ... }

W powyższym przykładzie trzeba dodać drugą zmienną typową do deklaracji typu B, żeby to przeszło.

24 października 2006, 20:14:35 Dodaj komentarz

MakeGenericType anyone?

Klepanie

Potrzebuję bardzo prostej rzeczy: mając obiekt klasy Type reprezentujący interesujący mnie typ T chcę uzyskać obiekt klasy Type reprezentujący typ List<T>. Ja to robię tak:

    Type MakeListType(Type elementType)
    {
        return typeof(List<int>).GetGenericTypeDefinition()
            .MakeGenericType(new Type[] { elementType });
    }

Da się to jakoś ładniej osiągnąć?

16 października 2006, 19:15:31 Dodaj komentarz

Delegaty w C#

Czepianie, Klepanie

Delegaty jako element języka C# zawsze wydawały mi się niewygodne w użyciu. Nie jestem w stanie zrozumieć logiki ich podwójnej deklaracji:

    public delegate TOutput Converter<TInput,TOutput> (TInput input);
    public List<TOutput> ConvertAll<TOutput> (Converter<T,TOutput> converter)

Tak, jakby TOutput converter<T,TOutput>(T element) nie było dobrym argumentem dla ConvertAll.

I musze zmartwić panów projektantów z Microsoftu (choć tego nie czytają, więc ich raczej nie obejdzie), że syntax highlighting w Visual Studio niewiele pomaga - deklaracja delegatu zawsze będzie mi się mylić z deklaracjami zmiennych i (jako osobie przyzwyczajonej do wskaźników do funkcji z C oraz do deklaratorów z Nemerle) zawsze będzie mi to wadzić (o słabiutkiej inferencji typów w C# w ogóle nie chcę wspominać).

Kolejnym problemem delegatów w wydaniu Microsoftu jest niekompletna dokumentacja. To znaczy - informuje ona programistów, jak z nich korzystać, ale w przeciwieństwie do innych klas, brak jest odnośnika Members, a do hierarchii typów trzeba się dobierać z innej strony. W tej sytuacji, w charakterze dokumentacji korzystam z pary: kompilator-dissassembler oraz Reflectora (np. kompilator C# nie używa żadnego z konstruktorów klasy Converter'2, który byłby odziedziczony z którejś z nadrzędnych klas).

Cała ta gimnastyka przypomina mi trochę czasy implementowania obsługi SMTP albo POP3 - najpierw czytałem RFC, a potem obserwowałem co robią inne programy pocztowe. A ponieważ inne programy nie były zgodne z RFC, to dla dobra użytkownika ja również nie byłem.

07 października 2006, 13:57:20 2 komentarze

Backquotes dla Windows

Klepanie

Ku pamięci. Tak się robi podstawienie wyjścia programu do zmiennej w windowsowych batach:

FOR /F "tokens=*" %%G IN ('wywolanie programu z argumentami') DO SET ZMIENNA=%%G

Edit: Tak się oczywiście robi przypisanie ostatniego wiersza wyjścia.

02 października 2006, 10:48:04 Dodaj komentarz

Reflection Emit, czyli narzekania na Microsoft ciąg dalszy

Czepianie, Klepanie

Resztki mojego wewnętrznego przekonania, że programiści Microsoftu przygotowują elegancki kod i wspaniałe API (patrz poprzednie przygody), odeszły dzisiaj całkowicie do lamusa. Od rana jeden z moich kolegów siedział nad rozszerzeniami do Visual Studio. Klął przy tym niemiłosiernie bo dokumentacji do tego brak jakiejkolwiek, a do części funkcjonalności po prostu nie sposób się dobrać.

Ja tymczasem siedzę nad generatorem kodu i natrafiłem na kolejne kwiatki w moim ulubionym nejmspejsie. Po pierwsze radzę uważać na argumenty przekazywane do metody Emit, bo mogą nadpisać następne instrukcje. Konkretnie chodzi o tego typu wywołanie: ilGenerator.Emit(OpCodes.Ldarg, num). Jeśli num jest mniejszy od inta, to następne wygenerowane instrukcje zostaną potraktowane jako fragment argumentu instrukcji Ldarg... no ale tak to jest, jak się robi tyle przeciążeń. Po drugie zaś dowiedziałem się, że nie przewidziano API umożliwiającego dostęp do GACa. Jak poinformowali nas autorzy dokumentacji:

If you use the GAC, this directly exposes your application to assembly binding fragility or may cause your application to work improperly on future versions of the .NET Framework. (...) The actual storage location and structure of the GAC is not documented and is subject to change in future versions of the .NET Framework and the Microsoft Windows operating system.

No nic... póki co najlepszym API dostępu do GACa jest chyba opakowanie sobie gacutila.

25 września 2006, 20:22:35 Dodaj komentarz

.NET Reflection c.d.

Klepanie

Wpadłem dziś na genialny(?) pomysł rozwiązania mojego wczorajszego problemu z builderami. Postanowiłem zaimplementować alternatywny TypeBuilder, który dziedziczy z Type (TypeBuilder jest sealed), a w środku trzyma sobie prawdziwego TypeBuildera i przekazuje mu wszystkie wywołania.

Zmarnowałem na tym około 2 godzin, żeby się zorientować, że część przeciążanych metod jest protected, więc nie da się ich przerzucić na TypeBuildera, a wypełnione być muszą, bo są wywoływane przez metody zaimplementowane w Type.... masakra.

Ostatecznie stwierdziłem, że opakowanie tego gówna jest najprostszym rozwiązaniem. Ale - jak to przy pakowaniu gówna - opakowanie jest niezbyt eleganckie od środka.

Do tej pory podziwiałem Microsoft za ich API. Przygotowane do najdrobniejszego szczegółu, super udokumentowane... zawiodłem się.

31 sierpnia 2006, 19:19:43 Dodaj komentarz

Pytanie o Reflection

Klepanie

Ktoś w ekipie układającej API dotnetowego reflection wpadł na bardzo fajny pomysł, żeby wszystkie XBuildery dziedziczyły po odpowiadających im typach X. I tak TypeBuilder dziedziczy po Type, AssemblyBuilder dziedziczy po Assembly, MethodBuilder dziedziczy po MethodInfo itd. Sprytne, nie?

Zauroczony programista siada zatem i pisze... zakładając że skoro inżynierowie Microsoftu tak ładnie to zaprojektowali, to równie ładnie to zaimplementowali. Wszystko sobie fajnie zaplanował, ułożył struktury danych itd... aż do momentu, kiedy po raz pierwszy uruchomi swój program.

Oto nagle okazuje się, że próba wywołania metody GetMethods na obiekcie klasy Type zwraca metody reprezentowanego przez niego typu, ale ta sama metoda wywołana na obiekcie typu TypeBuilder zgłasza wyjątek NotSupportedException. WTF? - ja się pytam. Jak mam to rozumieć? Nie umieliśmy tego zaimplementować? Nie chciało nam się tego zaimplementować? Czy ktoś mi może wytłumaczyć skąd takie zachowanie? Dlaczego muszę sam implementować coś, co (według MSDNu) mam w bibliotece? Dlaczego dokumentacja milczy na ten temat?

30 sierpnia 2006, 21:20:13 Dodaj komentarz