czy w boost jest jakieś gotowe rzutowanie dynamicznie
sprawdzające zakresy przy konwersji między typami
całkowitymi? Np.:
unsigned int u = safe_cast<unsigned int>(-1);
powinno rzucić wyjątkiem std::runtime_error lub
czymś podobnym. Napisanie tego porządnie samemu
to kilka godzin pracy, więc wolałbym gotowca. :-)
Pozdrawiam
Piotr Wyderski
Piotr Wyderski wrote:
> czy w boost jest jakieś gotowe rzutowanie dynamicznie
> sprawdzające zakresy przy konwersji między typami
> całkowitymi? Np.:
A niemożesz użyć czegoś jak na stronach poniżej, aby typ był po prostu
bezpieczny?
http://www.msobczak.com/prog/bin/range.tar.gz
http://www.msobczak.com/prog/typegen/
Pozdrawiam.
--
|\/\/| Seweryn Habdank-Wojewódzki
`\/\/'
> A niemożesz użyć czegoś jak na stronach poniżej, aby typ był po prostu
> bezpieczny?
Niestety nie, dostosowuję istniejący kod.
Pozdrawiam
Piotr Wyderski
>> A niemożesz użyć czegoś jak na stronach poniżej, aby typ był po prostu
>> bezpieczny?
>
> Niestety nie, dostosowuję istniejący kod.
Zgadzam się, że coś takiego (safe_cast albo może lepiej numeric_cast) by
się przydało.
Wrzuć ten temat na boost-devel, jutro Ci ktoś napisze. ;-)
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
> Zgadzam się, że coś takiego (safe_cast albo może lepiej numeric_cast) by
> się przydało.
> Wrzuć ten temat na boost-devel, jutro Ci ktoś napisze. ;-)
:-)
Już sam zacząłem to robić. Myślałem, że jest jakiś gotowiec
w boost, bo rzecz nie wygląda na nietypową, ale skoro go
nie ma, to z przyjemnościa go napiszę. Swoją drogą, to
implementacja robi się bardziej skomplikowana niż się
pierwotnie wydawało.
Pozdrawiam
Piotr Wyderski
Dla typów wbudowanych nie jest takie skomplikowane, dla pozostałych (z
operatorem konwersji) chyba nie ma sensu sprawdzać...
Moja propozycja wygląda tak:
#include <limits>
#include <stdexcept>
template <int N>
struct Int2Type {
enum { value = N };
};
namespace detail {
template <typename TargetType, typename SourceType>
inline TargetType numeric_cast(const SourceType& in, Int2Type<true>)
{
if ( in > std::numeric_limits<TargetType>::max() ||
in < std::numeric_limits<TargetType>::min())
{
throw std::runtime_error("bad numeric cast");
}
return in;
}
template <typename TargetType, typename SourceType>
inline TargetType numeric_cast(const SourceType& in, Int2Type<false>)
{
return in;
}
} //namespace
template <typename TargetType, typename SourceType>
inline TargetType numeric_cast(const SourceType& in)
{
detail::numeric_cast<TargetType>(in,
Int2Type<std::numeric_limits<SourceType>::is_specialized
&& std::numeric_limits<TargetType>::is_specialized>());
}
Pozdrawiam,
BX
> Moja propozycja wygląda tak:
> if ( in > std::numeric_limits<TargetType>::max() ||
> in < std::numeric_limits<TargetType>::min())
Nie zadziała gdy typ źródłowy jest signed a drugi unsigned i gdy in
będzie poza zakresem tego drugiego, bo na skutek konwersji zostanie
przeinterpretowany bit znaku.
#include <iostream>
#include <limits>
int main()
{
int in = -7;
if (in > std::numeric_limits<unsigned int>::max() ||
in < std::numeric_limits<unsigned int>::min())
{
std::cout << "Shit, " << in
<< " is out of unsigned int!\n";
}
else
{
std::cout << "OK and cool, " << in
<< " fits into unsigned int.\n";
}
}
$ ./a.out
OK and cool, -7 fits into unsigned int.
$
To nie jest takie hop-siup. :-)
Maciej Sobczak wrote:
> Nie zadziała gdy typ źródłowy jest signed a drugi unsigned i gdy in
> będzie poza zakresem tego drugiego, bo na skutek konwersji zostanie
> przeinterpretowany bit znaku.
A nie można tak po prostu napisać makro generujące wszystkie specjalizacje
dla typów signed i unsigned?
Albo jakoś dynamicznie sprawdzać czy typ jest signed czy unsigend. Kiedyś
była dyskusja o endiannes i nagle okazało się że to jest pikuś, może z
signed i unsigned też jest banał. Powinno to wyjść poprzez test czy rzut
(-1) na typ jest dalej (-1) albo czy jest < 0, albo jakiś przygłupawy w
każdym razie np. (type)(-1) > 2 * 1, czy nie?
> A nie można tak po prostu napisać makro generujące wszystkie specjalizacje
> dla typów signed i unsigned?
Sporo ich będzie.
> Albo jakoś dynamicznie sprawdzać czy typ jest signed czy unsigend.
Przecież z łatwością da się ro sprawdzić statycznie.
W numeric_limits masz składową is_signed.
> może z signed i unsigned też jest banał.
Bo to koncepcyjnie jest banał, tylko implementacja jest dość żmudna.
Trzeba zadbać o właściwe konwersje między typami z i bez znaku,
nie sprawdzać min() i max() tam, gdzie tego nie trzeba (co już daje 8
specjalizacji), dodać warianty do konwersji z i na bool itd.
Ja się nie pytałem o to, jak to zrobić, bo to wiem, tylko czy istnieje
gotowiec. Ale wygląda na to, że go nie ma, więc czeka mnie trochę
własnoręcznej rzeźby. :-)
Pozdrawiam
Piotr Wyderski
Piotr Wyderski wrote:
> Ja się nie pytałem o to, jak to zrobić, bo to wiem, tylko czy istnieje
> gotowiec. Ale wygląda na to, że go nie ma, więc czeka mnie trochę
> własnoręcznej rzeźby. :-)
Rozumiem. Ale sugeruję, że to będzie sprawa napisania stosownego makra oraz
może szablonu w C++. Oraz w najgorszym przypadku w pythonie generatora
kodu, aby wypełnić macierz konwersji typów i zapakować to do jednego
headera.
Nie przyglądałem się, jak napisać najzgrabniej taki kawałek kodu, ale wydaje
mi się, że po napisaniu jednej dwóch konwersji to jest będzie arcycepowate
rozwiązanie. Więc można napisać sobie generator kodu. Nawet nie bedziesz
się musiał wiele napisać. Typów standardowych jest niewiele. Typów
windowsa, jest trochę. Linuxa nie używasz, więc olewasz typy standardowe w
linuxie :-D.
> Nie przyglądałem się, jak napisać najzgrabniej taki kawałek kodu
Mnie zajęło to 239 linii, ale sport część tego to specjalna
obsługa booli oraz furtki dla przyszłych rozszerzeń.
> Linuxa nie używasz
Nie używam jako ceniący sobie wygodę user, ale jako programista używam. ;-)
Pozdrawiam
Piotr Wyderski
Piotr Wyderski wrote:
> Mnie zajęło to 239 linii, ale sport część tego to specjalna
> obsługa booli oraz furtki dla przyszłych rozszerzeń.
A tak przy okazji, znalazłem pole w numeric_limits<T>::is_signed, czy ono
nie daje odpowiedzi na to pytanie?
> To nie jest takie hop-siup. :-)
...faktycznie, trzeba obsłużyć jeszcze pare przypadków
For every complex problem, there is a solution that is simple, neat, and
wrong : )
Pozdrawiam
BX
> A tak przy okazji, znalazłem pole w numeric_limits<T>::is_signed, czy ono
> nie daje odpowiedzi na to pytanie?
Ale na jakie pytanie? :-D
Pozdrawiam
Piotr Wyderski
Piotr Wyderski wrote:
> Seweryn Habdank-Wojewódzki wrote:
>
>> A tak przy okazji, znalazłem pole w numeric_limits<T>::is_signed, czy ono
>> nie daje odpowiedzi na to pytanie?
>
> Ale na jakie pytanie? :-D
BobyX napisał szablon, który kontroluje zakresy. A skolei Maciek podnios
problem signed i unsigned. I tu znowu można zrobić specjalizację szablonu
kiedy jest zgodność, signed albo unsigned typów. A kiedy nie ma to trzeba
zawężać zakres, tak aby:
int a=-1
unsigbed int b = numeric_cast<int>(a);
sypnęło wyjątkiem.
> int a=-1
> unsigbed int b = numeric_cast<int>(a);
>
> sypnęło wyjątkiem.
Niestety to akurat nie sypnie - zapomniałeś unsigned w numeric_cast,
dzięki czemu numeric_cast nie robi nic a problem sprowadzi się ponownie
do standardowej konwersji. To właśnie pokazuje, że ten schemat jest
zbudowany na słabych fundamentach.
Niestety, ale dymyślne konswersje dla typów wbudowanych są zwalone.
Jeżeli już ktoś i tak musi się tymi typami posługiwać, to może liczyć co
najwyżej na warningi od kompilatora.
Maciej Sobczak wrote:
> Niestety to akurat nie sypnie - zapomniałeś unsigned w numeric_cast,
Tak. poprawiam się,
int a=-1
unsigbed int b = numeric_cast<unsigned int>(a);
Dla podkreślenia. Mam na myśli konkretyzację:
numeric_cast<unsigned int>( int a ).
Powinno sypnąć, prawda? To znaczy istnieje implementacja taka
numeric_cast<T>(), która powinna sypnąć wyjątkiem.
Czy ja czegoś nie rozumiem?
> Niestety, ale dymyślne konswersje dla typów wbudowanych są zwalone.
> Jeżeli już ktoś i tak musi się tymi typami posługiwać, to może liczyć co
> najwyżej na warningi od kompilatora.
Tak. Ja akurat jestem w tej dobrej sytuacji, że sypie mi warningami.
> Maciej Sobczak wrote:
> > Nie zadziała gdy typ źródłowy jest signed a drugi unsigned i gdy in
> > będzie poza zakresem tego drugiego, bo na skutek konwersji zostanie
> > przeinterpretowany bit znaku.
> A nie można tak po prostu napisać makro generujące wszystkie specjalizacje
> dla typów signed i unsigned?
A nie można po prostu napisać jednego wzorca, który na podstawie typu podanego
argumentu pobierze z niego std::numeric_limits?
> Albo jakoś dynamicznie sprawdzać czy typ jest signed czy unsigend.
Po pierwsze, dynamicznie tego się nie da sprawdzić - jak wartość unsigned int
przypisałeś do zmiennej signed int to już jest dawno po ogórkach.
Po drugie, nie ma po co sprawdzać dynamicznie, skoro ten typ jest znany w
czasie kompilacji. Jeśli nie - to patrz linijka wyżej.
--
// _ ___ Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `| /^\ ,() <ethouris(O)gmail.com>
// \_ |\ \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"Java is answer for a question that has never been stated"
Sektor van Skijlen wrote:
> A nie można po prostu napisać jednego wzorca, który na podstawie typu
> podanego argumentu pobierze z niego std::numeric_limits?
Więc właśnie tego nie kapuję.
zgrubsza chodzi o sytuację jak dla int:
int = numeric_cast<int>( /* unsigned long */ x )
tak, żeby słał wyjątki kiedy x < 0 [czyli numeric_limits<unsigned
long>::min()] oraz kiedy x > numeric_limits<int>::max();
Sztuka polega, na tym aby to jakoś zgrabnie zapiąć w szablon jeszcze nie
czuję sprawy, ale jakby szablon mógł rozwijać wyrażenie (exp ? expr1 :
expr2) to powinno nie być trudne, bo wystarczy jako ograniczenie dolne
wybrać
lower_bound = max ( min_type_1, min_type_2 )
upper_bound = min ( max_type_1, max_type_2 )
I koniec, chodzi o to, aby to byo znane w czasie kompilacji, bo bezsęsu to
robić w runtime, kiedy i tak nie ma się wpływu na granice POD w runtime.
> Sektor van Skijlen wrote:
> > A nie można po prostu napisać jednego wzorca, który na podstawie typu
> > podanego argumentu pobierze z niego std::numeric_limits?
> Więc właśnie tego nie kapuję.
> zgrubsza chodzi o sytuację jak dla int:
> int = numeric_cast<int>( /* unsigned long */ x )
> tak, żeby słał wyjątki kiedy x < 0 [czyli numeric_limits<unsigned
> long>::min()] oraz kiedy x > numeric_limits<int>::max();
template <class Target, class Source>
Target numeric_cast( Source x )
{
if ( std::numeric_limits<Source ...
}
Nie kończę, bo to wymaga pomyślunku, jak napisałeś niżej, a w tej chwili mi
się nie chce :)
> Sztuka polega, na tym aby to jakoś zgrabnie zapiąć w szablon jeszcze nie
> czuję sprawy, ale jakby szablon mógł rozwijać wyrażenie (exp ? expr1 :
> expr2) to powinno nie być trudne, bo wystarczy jako ograniczenie dolne
> wybrać
> lower_bound = max ( min_type_1, min_type_2 )
> upper_bound = min ( max_type_1, max_type_2 )
W zasadzie to masz tak:
1. Załóżmy, że masz pewność, iż wartość w typie źródłowym mieści się w
numeric_limits
2. Teraz trzeba sprawdzić, czy wartość źródłowa "mieściłaby się" w
numeric_limits typu docelowego
3. W tym celu potrzebujemy mieć zakresy dla typu docelowego przekonwertowane
na wartości typu źródłowego :)
I tu niestety klapa. Chociaż, gdyby się temu uważniej przyjrzeć, to nie jest
jeszcze tak źle. Pierwsza rzecz, jaką musisz sprawdzić, to is_signed. I wtedy:
- jeśli mają taką samą signedness, to musisz sprawdzić "na szerokość"
- jeśli tylko typ docelowy jest unsigned, to zamiast dolnego zakresu
sprawdzasz, czy wartość źródłowa nie jest ujemna
- jeśli typ źródłowy jest unsigned do w ogóle nie sprawdzasz dolnego zakresu
To jest trochę ekwilibrystyka, niestety, właśnie dlatego, że nie możesz
bezpośrednio przekonwertować zakresów dla wartości docelowej na typ źródłowy,
bo jakby to było takie proste, to mógłbyś w ogóle konwertować bez sprawdzania
zakresów. Konwersja wartości zakresów jest niestety obarczona tym samym
ryzykiem co konwersja interesującej nas wartości.
> I koniec, chodzi o to, aby to byo znane w czasie kompilacji, bo bezsęsu to
> robić w runtime, kiedy i tak nie ma się wpływu na granice POD w runtime.
No to czeka cię jeszcze lepsza ekwilibrystyka z metaprogramowaniem (różne
specjalizacje dla typów różnej szerokości) - ale jest to wykonalne.
>> A niemożesz użyć czegoś jak na stronach poniżej, aby typ był po prostu
>> bezpieczny?
>
> Niestety nie, dostosowuję istniejący kod.
Czytam te wszystkie posty i się dziwię. Co to znaczy - dostosowuję
istniejący kod? Wstawiasz numeric_cast gdzie popadnie?
Czy nie prościej zrobić (albo wygenerować) wrappery do użytych typów i
zmodyfikować tylko deklaracje zmiennych zamiast modyfikować wszystkie
wyrażenia?
Obstawiam, że deklaracji jest mniej i łatwiej je znaleźć, niż wszystkie
miejsca niejawnych konwersji.
> Czytam te wszystkie posty i się dziwię.
Nie dziwię się, że się dziwisz. :-)
> Co to znaczy - dostosowuję istniejący kod?
Taki z asercjami sizeof(std::size_t) == sizeof(uint32_t)... :-/
> Czy nie prościej zrobić (albo wygenerować) wrappery do użytych typów i
> zmodyfikować tylko deklaracje zmiennych zamiast modyfikować wszystkie
> wyrażenia?
Nie, bo musi zostać zachowana spójność na poziomie serializacji.
Pozdrawiam
Piotr Wyderski
> Niestety, ale dymyślne konswersje dla typów wbudowanych są zwalone.
Powiedziałbym nawet, że to same typy wbudowane są
zwalone, ale nie chce mi się odgrzewać starych flejmów.
No, może jednak odrobinkę: co stoi na przeszkodzie
dodania przez Komitet do standardu języka appendixu
doprecyzowującego semantykę większości UB? Widzę
to tak: główny standard języka rozwija się jak dotychczas,
po staremu wszelkimi sposobami wymykając się jak
piskorz od udzielenia odpowiedzi na pytania "co to jest
int? ile ma bitów, jakie kodowanie?, co się stanie, gdy
do INT_MAX dodam 1?" i tysiące innych, natomiast
załącznik na podstawie dekad rozwoju hardware dokładnie
określa brzmienie odpowiedzi, jak to ma miejsce w Javie
i C#. Dzięki temu można jednocześnie mieć ciastko i zjeść
ciastko -- przed rozpoczęciem prac nad nowym projektem
podejmie się decyzję, z gwarancji której części standardu
korzystamy i wszyscy będą zadowoleni. Puryści i fanatycy
starego ładu (bo i tacy się pojawią :-)) będą mogli pisać
tak, jak to robili do tej pory, a pragmatycy będą mogli
spać spokojnie, bez zrywania się z krzykiem, że przyśniły
im się się niedostatecznie pojemne liczby całkowite,
awaryjne zatrzymanie biegu programu z powodu przepełnienia
numerycznego, operacji logicznych na wskaźnikach itd.
Sam C++ na tym nic nie straci, bo UB dopuszczalne
zachowanie, a więc między innymi _takie_, jak w załączniku.
W nagłówkach wymagających większych gwarancji dopisze
się po prostu
#pragma common_sense_cplusplus
i będzie się można cieszyć się zdeubekizowanym dialektem języka...
Jeśli zaś się tego nie dopisze, to semantyka delikatnych konstrukcji
będzie taka, jak dawniej, a więc właściwie jej nie będzie.
> Jeżeli już ktoś i tak musi się tymi typami posługiwać, to może liczyć co
> najwyżej na warningi od kompilatora.
Ostrożnie, ta sprawa jest bardzo delikatna i nie da się jej zbyć
jednym zdaniem komentarza. Nie masz wpływu m.in. na definicję
std::vector<T>::size_type. Chcąc wprowadzić możliwość wymiany
danych pomiędzy wersjami tego samego programu skompilowanymi
jako 32- i 64-bitowe pojawia się spory problem, bo w jaki sposób
deserializować size_t? Można przyciąć do uint32_t, ale wówczas
pojawia się możliwość, że 64-bitowy wektor zawiera 2^32+ elementów
i dostaniemy przepełnienie. Można też rozszerzyć do uint64_t, ale
dlaczego akurat do tego typu? A co, jeśli ktoś zrobi porting na
maszynę 128-bitową? -- 2^64 elementów znów się nie zmieści.
Teoretyczie najlepszym rozwiązaniem będzie użycie liczb całkowitych
wielokrotnej precyzji, ale wówczas komunikacja będzie działać
z prędkością warpową i masz spore szanse, że o zmroku do Twoich
drzwi zastuka tłum uzbrojonych w widły i pochodnie użytkowników
Twojej biblioteki. Cóż, jak nie urok, to... :-)
Pozdrawiam
Piotr Wyderski
>> Co to znaczy - dostosowuję istniejący kod?
>
> Taki z asercjami sizeof(std::size_t) == sizeof(uint32_t)... :-/
Da się.
>> Czy nie prościej zrobić (albo wygenerować) wrappery do użytych typów i
>> zmodyfikować tylko deklaracje zmiennych zamiast modyfikować wszystkie
>> wyrażenia?
>
> Nie, bo musi zostać zachowana spójność na poziomie serializacji.
Zrób wrapperek jako PODa, gdzie T będzie jedynym polem.
> Powiedziałbym nawet, że to same typy wbudowane są
> zwalone, ale nie chce mi się odgrzewać starych flejmów.
Eeee, typy wbydowane nie są tragiczne. To ich wzajemne relacje i
interakcje czynią je słabymi.
> No, może jednak odrobinkę: co stoi na przeszkodzie
> dodania przez Komitet do standardu języka appendixu
> doprecyzowującego semantykę większości UB?
Producenci kompilatorów to robią. Mogą.
> Widzę
> to tak: główny standard języka rozwija się jak dotychczas,
> po staremu wszelkimi sposobami wymykając się jak
> piskorz od udzielenia odpowiedzi na pytania "co to jest
> int? ile ma bitów, jakie kodowanie?, co się stanie, gdy
> do INT_MAX dodam 1?"
Standard ma zastosowanie do różnych sprzętów, również tych
bezsensownych. Normalni ludzi jednak piszą na jakiś podzbiór platform,
gdzie ich architektura podaje brakujące odpowiedzi.
> natomiast
> załącznik na podstawie dekad rozwoju hardware dokładnie
> określa brzmienie odpowiedzi, jak to ma miejsce w Javie
> i C#.
Java i C# są tak nieprzenośne, że z podobnym efektem można skoncentrować
się na jednym kompilatorze C++, który też udziela jasnych odpowiedzi na
Twoje pytania.
> Dzięki temu można jednocześnie mieć ciastko i zjeść
> ciastko -- przed rozpoczęciem prac nad nowym projektem
> podejmie się decyzję, z gwarancji której części standardu
> korzystamy i wszyscy będą zadowoleni.
Korzystamy z tych gwarancji, które dają nam kompilatory.
Oprócz "Hello World" robią tak chyba wszystkie programy.
> Puryści i fanatycy
> starego ładu (bo i tacy się pojawią :-)) będą mogli pisać
> tak, jak to robili do tej pory, a pragmatycy będą mogli
> spać spokojnie, bez zrywania się z krzykiem, że przyśniły
> im się się niedostatecznie pojemne liczby całkowite,
> awaryjne zatrzymanie biegu programu z powodu przepełnienia
> numerycznego, operacji logicznych na wskaźnikach itd.
Obudziłeś się kiedyś z tego powodu? Ja nie. :-)
>> Jeżeli już ktoś i tak musi się tymi typami posługiwać, to może liczyć
>> co najwyżej na warningi od kompilatora.
>
> Ostrożnie, ta sprawa jest bardzo delikatna i nie da się jej zbyć
> jednym zdaniem komentarza. Nie masz wpływu m.in. na definicję
> std::vector<T>::size_type. Chcąc wprowadzić możliwość wymiany
> danych pomiędzy wersjami tego samego programu skompilowanymi
> jako 32- i 64-bitowe pojawia się spory problem, bo w jaki sposób
> deserializować size_t?
Tekstowo. XML jest modny ostatnio. Albo binarnie, używając
jakiegokolwiek standardu (XDR, ASN.1).
BTW - jak sobie radzisz z serializacją pomiędzy Javą i C#? :-P
> Teoretyczie najlepszym rozwiązaniem będzie użycie liczb całkowitych
> wielokrotnej precyzji, ale wówczas komunikacja będzie działać
> z prędkością warpową
Serializacja nie jest tutaj ograniczeniem prędkości. Jeżeli podajesz już
przykłady wektorów z 128-bitowym rozmiarem, to na moje oko
serializowanie *jednego pola* typu size_type nie przeraża mnie w
kontekście tego wszystkiego, co musi polecieć zaraz za nim.
Jak zwykle piętrzysz problemy. :-)
>> No, może jednak odrobinkę: co stoi na przeszkodzie
>> dodania przez Komitet do standardu języka appendixu
>> doprecyzowującego semantykę większości UB?
>
> Producenci kompilatorów to robią. Mogą.
Ale te dodatki obowiązują tylko w obrębie danego kompilatora,
a tymczasem ja chcę mieć program przenośny, choć korzystający
ze znacznie silniejszych gwarancji, niż dotychczasowe. Efektem
będzie oczywiście mniejsza liczba platform docelowych, ale
w praktyce kompletnie nie ma to znaczenia, bo bezsensowne
platformy i tak są na wymarciu.
> Standard ma zastosowanie do różnych sprzętów, również tych bezsensownych.
I właśnie o to chodzi, by dalej sobie miał. Jedynie na
platformach sensownych dojdzie pewien zbiór dodatkowych
gwarancji. Skorzystanie z nich będzie dobrowolne, choć
nie mam wątpliwości, co wybierze przemysł. :-)
> Normalni ludzi jednak piszą na jakiś podzbiór platform, gdzie ich
> architektura podaje brakujące odpowiedzi.
Dokładnie o to chodzi. Więc należy dać im te gwarancje,
bo i tak się nic na tym nie traci. Podkreślam, chodzi o
dołączenie do standardu rozdziału dającego dodatkowe
gwarancje dla osób gotowych zrezygnować z "dziwnych"
platform, a nie o przedefiniowanie pozostałej części standardu.
Właśnie na tym polega sztuczka. :-)
> Java i C# są tak nieprzenośne
Jasne, zwłaszcza ta pierwsza...
> Korzystamy z tych gwarancji, które dają nam kompilatory.
... w obrębie danego kompilatora.
> Oprócz "Hello World" robią tak chyba wszystkie programy.
Robią, bo muszą, stąd setki headerów z #ifdefami...
> Tekstowo. XML jest modny ostatnio.
Ale nie o reprezentację chodzi, tylko o to, do jakiego typu
deserializować zapisaną wartość i co zrobić, jeśli przekracza
pojemność żądanego typu docelowego (np. std::size_t).
> BTW - jak sobie radzisz z serializacją pomiędzy Javą i C#? :-P
Mapuję typy C++ na typy javowe i działa bez pudła?
> Serializacja nie jest tutaj ograniczeniem prędkości.
Może być.
> Jeżeli podajesz już przykłady wektorów z 128-bitowym rozmiarem,
> to na moje oko serializowanie *jednego pola* typu size_type nie przeraża
> mnie w kontekście tego wszystkiego, co musi polecieć zaraz za nim.
Nie o to chodzi. Problemem jest to, co zrobić z size_t...
Pozdrawiam
Piotr Wyderski
Ale przecież program, w którym vector<...>::size_t ma 32 bity i tak nie jest
w stanie przechować obiektów z drugiego wektora, w którym tych danych jest
2^32+n, n >= 1, więc w czym problem? Albo jesteś gotowy na taką ilość danych
i używasz własnej implementacji wektora, albo nie jesteś, i zgłaszasz błąd
deserializacji.
A gwarancje, o jakich pisałeś, na pewno by się przydały.
JD
> Ale przecież program, w którym vector<...>::size_t ma 32 bity i tak nie
> jest
> w stanie przechować obiektów z drugiego wektora, w którym tych danych jest
> 2^32+n, n >= 1
n >= 0, 2^32 też się nie zmieści. ;-)
> więc w czym problem?
Problem jest właśnie w ustaleniu, co jest problemem. :-)
Można przyjąć, że przy serializacji rozszerzamy size_t
do np. 64 bitów, ale wówczas wersja 32-bitowa musi
odpowiednio zareagować na tę sytuację. Można też
przyciąć size_t do 32 bitów i zapewnić, że żaden kontener
nie będzie miał więcej elementów. Wady są oczywiste,
ale zaletą jest bliższe naśladowanie interfejsu Javy, która
używa typu int do określania rozmiaru kolekcji.
> A gwarancje, o jakich pisałeś, na pewno by się przydały.
I da się je wprowadzić bez rewolucji, której zwolennikiem byłem wcześniej.
:-)
Suplement do standardu w postaci opcjonalnie wykorzystywanego załącznika
idealnie załątwia sprawę.
Pozdrawiam
Piotr Wyderski
>> Producenci kompilatorów to robią. Mogą.
>
> Ale te dodatki obowiązują tylko w obrębie danego kompilatora
Albo w obrębie wielu kompilatorów, które dają takie same gwarancje. W
czym problem?
>> Standard ma zastosowanie do różnych sprzętów, również tych bezsensownych.
>
> I właśnie o to chodzi, by dalej sobie miał. Jedynie na
> platformach sensownych dojdzie pewien zbiór dodatkowych
> gwarancji. Skorzystanie z nich będzie dobrowolne, choć
> nie mam wątpliwości, co wybierze przemysł. :-)
No przecież przemysł wybiera - wszystkie znane mi kompilatory dają
zgodne gwarancje w większości interesujących mnie obszarów.
> Podkreślam, chodzi o
> dołączenie do standardu rozdziału dającego dodatkowe
> gwarancje dla osób gotowych zrezygnować z "dziwnych"
> platform, a nie o przedefiniowanie pozostałej części standardu.
> Właśnie na tym polega sztuczka. :-)
To po co to wstawiać do standardu? Zrób sobie osobny dokument pt.
"Recommendations for implementers of the C++ programming language for
platform X, Y and Z".
Zdaje się, że Intel nawet coś takiego popełnił.
>> Java i C# są tak nieprzenośne
>
> Jasne, zwłaszcza ta pierwsza...
Zwłaszcza ta pierwsza nie działa na żadnym z tysiąca komputerów, którymi
się zajmuje moja grupa w pracy. Już ten temat przerabialiśmy.
>> Korzystamy z tych gwarancji, które dają nam kompilatory.
>
> ... w obrębie danego kompilatora.
W obrębie wielu kompilatorów.
>> Oprócz "Hello World" robią tak chyba wszystkie programy.
>
> Robią, bo muszą, stąd setki headerów z #ifdefami...
Z mojej skromnej praktyki wynika, że #ifdefy rozwiązują głównie problem
niezgodności między systemami operacyjnymi, nawet jeśli sprzęt pod nimi
jest ten sam. To jest shit, ale nie ja go wymyśliłem.
Chociaż faktem jest też, że np. kod Boosta ma masę omijaczy na różne
kompilatory - ale to dotyczy raczej bugów w kompilatorach a nie
gwarancji dla typów podstawowych (a o tym mowa).
>> Tekstowo. XML jest modny ostatnio.
>
> Ale nie o reprezentację chodzi, tylko o to, do jakiego typu
> deserializować zapisaną wartość i co zrobić, jeśli przekracza
> pojemność żądanego typu docelowego (np. std::size_t).
To i tak jesteś w błocie. Nie ma znaczenia, czy sobie wymyślisz własny
typ na tą okoliczność, jak np. w Adzie:
type My_Size_Type is range 0..10000000000000000000000000000000;
(co AFAIK nie musi wcale zadziałać)
bo ograniczenie *i tak* jest arbitralne po stronie odbiorcy - co
zrobisz, jeśli deserializowana wartość jest poza tym arbitralnie
wybranym zakresem?
Podobnie, std::size_t ma arbitralną wartość na kompilatorze, którego
używasz - to jest ten sam problem. Albo potrafisz przewidzieć w jakich
warunkach będzie działać Twój system, albo nie potrafisz. W tym drugim
przypadku i tak ugrzęzłeś.
>> BTW - jak sobie radzisz z serializacją pomiędzy Javą i C#? :-P
>
> Mapuję typy C++ na typy javowe i działa bez pudła?
Jakie typy C++, skoro standard nie określa ich rozmiarów? :-P
>> Jeżeli podajesz już przykłady wektorów z 128-bitowym rozmiarem,
>> to na moje oko serializowanie *jednego pola* typu size_type nie
>> przeraża mnie w kontekście tego wszystkiego, co musi polecieć zaraz za
>> nim.
>
> Nie o to chodzi. Problemem jest to, co zrobić z size_t...
Nic. Przyjmij, że std::size_t ma maksymalny rozmiar, który ma sens na
danej platformie jako ilość elementów dowolnej kolekcji (taka w zasadzie
jest intencja tego typu). Jeśli coś się w tym typie nie mieści, to tym
bardziej nie zmieści się w wektorze dana ilość elementów i nawet nie ma
po co ich deserializować. Proste.
> Albo w obrębie wielu kompilatorów, które dają takie same gwarancje.
Po pierwsze wielu nie znaczy wszystkich, a po drugie gdzie
masz te _gwarancje_ i kto odpowiada za spójnośc między
poszczególnymi rekomendacjami? Nikt? Ojej...
> W czym problem?
W tym, że jeśli napiszę sobie program opierający się na takich
założeniach i poinformuję kompilator o tym odpowiednia pragmą,
to oczekuję, że albo kompilator mi taki program odrzuci w całości,
bo platforma nie pasuje lub on nie rozumie tej pragmy, albo
wygeneruje mi kod o bardzo dokładnie okreslonej semantyce.
_Dowolny_ kompilator ma to potrafić bez szemranych założeń.
> To po co to wstawiać do standardu?
Bo:
-- w bardzo istotnym stopniu dotyczy języka;
-- zmiany w standardzie będą na bieżąco synchronizowane z załącznikiem;
-- ISO ma autorytet. Kogo w przypadku C++ obchodzą rekomendację
jakiegoś tam Intela, że litościwie nie wspomnę o moich?
> Zrób sobie osobny dokument pt. "Recommendations for implementers
> of the C++ programming language for platform X, Y and Z".
Właśnie chodzi o brak tego "X, Y and Z". Chodzi o specyfikację maszyny
abstrakcyjnej C++ zgodną z dotychczasowymi gwarancjami _oraz_ ze
stanem techniki 20 lat po powstaniu C++. _Jeśli_ docelowa architektura
rzeczywista spełnia tę specyfikację _oraz jesli_ na kompilatorze wymusi
się zgodność z załącznikiem, to dostaniesz program o bardzo dokładnie
okreslonej semantyce, praktycznie bez UB. W przeciwnym razie będziesz
miał zwykłe C++.
> Zwłaszcza ta pierwsza nie działa na żadnym z tysiąca komputerów, którymi
> się zajmuje moja grupa w pracy. Już ten temat przerabialiśmy.
To musisz mieć straszengo pecha, bo zazwyczaj działa ona bez problemu.
> W obrębie wielu kompilatorów.
Na zasadzie domniemania.
> bo ograniczenie *i tak* jest arbitralne po stronie odbiorcy
Nie wszędzie -- np. w Javie pojemność kontenerów jest opisywana
typem o ustalonym zakresie, mianowicie 32-bitowym intem w U2.
Rozwiązanie ma sporo wad, ale akurat problemu z serializacją nie ma.
> Jakie typy C++, skoro standard nie określa ich rozmiarów? :-P
Na int32_t, int64_t itd. Ale fakt -- nieokreślone są nie tylko
rozmiary, ale i kodowanie liczb ze znakiem.
> Jeśli coś się w tym typie nie mieści, to tym bardziej nie zmieści
> się w wektorze dana ilość elementów i nawet nie ma po co ich
> deserializować. Proste.
I to dokładnie robię, safe_cast<> mi to sprawdza -- pytałeś, po co mi to.
:-)
Pozdrawiam
Piotr Wyderski
>> Zwłaszcza ta pierwsza nie działa na żadnym z tysiąca
>> komputerów, którymi się zajmuje moja grupa w pracy.
>
> To musisz mieć straszengo pecha, bo zazwyczaj działa
> ona bez problemu.
Z bash.org [tłumaczenie własne]:
"Mówienie, że Java jest fajna, bo działa na wszystkich
platformach, to jak mówienie, że seks analny jest fajny,
bo działa na wszystkich płciach" ;J
--
SasQ
>>> Zwłaszcza ta pierwsza nie działa na żadnym z tysiąca
>>> komputerów, którymi się zajmuje moja grupa w pracy.
>> To musisz mieć straszengo pecha, bo zazwyczaj działa
>> ona bez problemu.
Zazwyczaj to nawet na FreeBSD nie działa i trzeba używać nieoficjalnie
zhaczonych binarek.
A jak się ma pecha, to jest LynxOS. Nie, nie możemy sobie zmienić.
> Z bash.org [tłumaczenie własne]:
> "Mówienie, że Java jest fajna, bo działa na wszystkich
> platformach, to jak mówienie, że seks analny jest fajny,
> bo działa na wszystkich płciach" ;J
Aż sobie to zapisałem. :D
> "Mówienie, że Java jest fajna, bo działa na wszystkich
> platformach, to jak mówienie, że seks analny jest fajny,
> bo działa na wszystkich płciach" ;J
No ale C++ szczyci się tym, że ze względu na znacznie mniej
ograniczeń nakładanych na sprzęt działa na jeszcze większej
liczbie platform, niż Java, która jest "wszędzie". O co więc
w jego przypadku należy rozszerzyć powyższą "myśl", o
zwierzęta? :-)
Pozdrawiam
Piotr Wyderski
W świetle tego, co często tu piszesz o nietypowych platformach i
nowym/starym sprzęcie, to podchodzą jeszcze trupy.
JD
Piotr Wyderski wrote:
> czy w boost jest jakieś gotowe rzutowanie dynamicznie
> sprawdzające zakresy przy konwersji między typami
> całkowitymi? Np.:
>
> unsigned int u = safe_cast<unsigned int>(-1);
Przez przypadek coś znalazłem:
http://www.boost.org/libs/numeric/conversion/doc/index.html
Warto zobaczyć na:
http://www.boost.org/libs/numeric/conversion/doc/converter.html#rchklogic