Posts tagged ‘typecasting’

Cokolwiek, czyli Boost.Any

2010-04-17 11:52

Zasadniczo w C++ zmienne mają jednoznacznie określone typy, znane w czasie kompilacji. Istnieją oczywiście pewne mechanizmy, które zdają się tę zasadę lekko naciągać (dziedziczenie i polimorfizm, szablony), ale bez znaczącej nieścisłości można przyjąć, że jest ona prawdziwa. W języku kompilowanym nie jest to zresztą nic nadzwyczajnego.
Z kolei w językach skryptowych i interpretowanych dość powszechne jest używanie zmiennych bez określonego (tj. jawnie podanego w deklaracji) typu. To, co obecnie przechowuje liczbę, za chwilę może zawierać ciąg znaków czy odwołanie do obiektu i nie ma z tym specjalnego problemu. Typy w takich językach oczywiście istnieją, ale mają je wartości, nie zaś zmienne.

Czasami coś podobnego – czyli zmienna mogąca przechowywać wartości różnego rodzaju – przydaje się i w C++. Wtedy niektórzy przypominają sobie o void*, ale “ogólny wskaźnik na coś” rzadko jest w tym przypadku szczytem marzeń. Jego podstawową wadą jest to, że wymaga on przechowania gdzieś poza nim informacji o docelowym typie wartości, na którą pokazuje – jeśli chcemy ją później wykorzystać, rzecz jasna. Równie poważną jest fakt, że mamy tu do czynienia z wskaźnikiem; pamięć, na którą on pokazuje, musi być kiedyś zwolniona.
Dlatego też lepszym rozwiązaniem jest biblioteka Any z Boosta, umożliwiająca korzystanie ze zmiennych “typu dowolnego”. Reprezentuje go klasa boost::any, mogąca przechowywać wartości prawie dowolnego rodzaju (wymaganiem jest głównie to, by ich typy posiadały zwykły konstruktor kopiujący):

  1. #include <boost/any.hpp>
  2. boost::any any1 = 5, any2 = std::string("Ala ma kota");
  3. boost::any foo = Foo(); // o ile Foo da się kopiować

Ponieważ jednak jest to wciąż C++, a nie język skryptowy, do wartości zapisanych w obiekcie any bezpośrednio dostać się nie da. W szczególności nie można liczyć na niejawne konwersje powyższych zmiennych do typów int, string czy Foo. Zamiast tego korzysta się ze specjalnego operatora rzutowania any_cast, który pozwala na potraktowanie wartości zapisanej w any zgodnie z jej właściwym typem:

  1. int x = 1 + boost::any_cast<int>(any1);

W przeciwieństwie do void*, próba jej reinterpretacji na inny typ danych skończy się wyjątkiem. Nie trzeba jednak polegać na jego łapaniu, jeśli docelowego typu nie jesteśmy pewni: boost::any pozwala też pobrać jego type_info (tj. to, co zwraca operator typeid) bez dokonywania rzutowania.

I to w sumie wszystko jeśli chodzi o tę klasę, a jednocześnie i całą bibliotekę. Mimo swej niewątpliwej prostoty jest ona bardzo przydatna tam, gdzie potrzebujemy zmiennych przechowujących różne rodzaje wartości. Warto więc się z nią zapoznać :)

Tags: , , ,
Author: Xion, posted under Programming » 9 comments

Własny operator rzutowania

2008-01-30 22:27

W C++ nadal można używać “tradycyjnego” rzutowania w postaci (typ)wyrażenie, bo cały czas zachowywana jest przynajmniej częściowa kompatybilność wstecz ze starym C. Jak wiadomo jednak, zaleca się raczej stosowanie nowych (tzn. wprowadzonych w C++; faktycznie są już one raczej stare) operatorów rzutowania, czyli static_cast, dynamic_cast, reinterpet_cast i const_cast. Jest tak między innymi dlatego, iż pozwalają one rozróżnić rzutowania bezpieczne – które można sprawdzić w czasie kompilacji i które zawsze “mają sens” – od pozostałych.
Składnia takiego rzutowania to oczywiście something_cast<typ>(wyrażenie). Jeśli przyjrzymy się jej bliżej, to zauważymy interesującą rzecz. Otóż jest to bardzo podobne do pewnej formy wywołania szablonu funkcji. Ten fakt plus pewne zasady dotyczące dedukcji argumentów takich szablonów sprawiają, że zasadniczo możemy w C++ tworzyć… własne operatory rzutowania.

Prawdopodobnie najbardziej znanym jest implicit_cast, czyli operator rzutowania niejawnego. Możemy go zdefiniować następująco:

  1. template <typename Dest, typename Src>
  2. inline Dest implicit_cast(const Src& arg)
  3. {
  4.    return arg;
  5. }

i wywoływać w bardzo porządny sposób:

  1. string str = implicit_cast<string>("Hello world");

Ze strony swojego dzialania operator ten nie jest specjalnie ciekawy, bo to przykład językowego purytanizmu czystej wody. implicit_cast wykonuje bowiem te konwersje, które kompilator i tak by przeprowadził niejawnie; widać to zresztą po kodzie odpowiadającej mu funkcji.
No właśnie – warto zauważyć, że jest w gruncie rzeczy zwykła funkcja szablonowa. Działa ona jak operator rzutowania, gdyż drugi z argumentów tego szablonu – typ źródłowy – może zostać wydedukowany z argumentu funkcji (czyli wyrażenia, które rzutujemy). Musimy jedynie podać pierwszy z nich, czyli typ docelowy – ale tego można się było spodziewać. Trzeba zaznaczyć, że kolejność obu parametrów szablonu musi być właśnie taka, ponieważ ich przestawienie spowodowałoby, że automatyczna dedukcja nie mogła by zostać przeprowadzona. Zachodzi ona bowiem począwszy od końcowych parametrów.

Prawdopodobnie taki własny operator rzutowania to przede wszystkim efektowna sztuczka. Nic więc dziwnego, że kilka przykładów (np. lexical_cast) jest częścią biblioteki Boost, która niemal w całości jest zbiorem takich językowych gadżetów :) Sam aczkolwiek wymyśliłem dosyć dawno temu operator “rzutowania bezwzględnego”, który wygląda mniej więcej tak:

  1. template <typename Dest, typename Src>
  2. inline Dest absolute_cast(const Src& arg)
  3. {
  4.    return *(reinterpret_cast<Dest*>(const_cast<Src*>(&arg)));
  5. }

Służy on do dosłownego rzutowania dowolnego typu na dowolny inny w sensie zmiany interpretacji bajtów. Zdaje się on działać dla większości typów w C++ (głównym wyjątkiem są typy referencyjne, co nie jest specjalną niespodzianką) i bywa całkiem przydatny.

Naturalnie można wymyślić jeszcze sporo innych niestandardowych operatorów rzutowania, jak choćby konwersje między napisami w różnych kodowaniach (ANSI, Unicode) czy do/z jakichś własnych typów, opakowujących klasy zewnętrzne. W każdym razie widać znowu (i pewnie nie ostatni raz), że C++ pozwala nam na bardzo dużo, skoro nawet te nieco rozwlekłe *_casty można jakoś oswoić do własnych potrzeb :]

Tags: , ,
Author: Xion, posted under Programming » Comments Off on Własny operator rzutowania
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.