znaczacy > comp.lang.* > comp.lang.c

Zbigniew Płotnicki (01.10.2010, 08:11)
W związku z tym, że zaprezentowane przeze mnie wzory dotyczące
wyliczenia wyrażeń UB spowodowały wiele kontrowersji, a chciałbym
zwrócić głównie uwagę na moją klasę chroniącą przed UB czasu
kompilacji i czasu wykonania, tworzę nowy wątek na grupie i zachęcam
do pisania w nim, ale już tylko na temat klasy Number.

Polecam moją klasę Number do zastosowania w skomplikowanych
algorytmach numerycznych. Można z niej korzystać w trybie debugowania,
a w trybie release podstawić w jej miejsce zwykły typ numeryczny.
Wydaje mi się, że będzie ona użyteczna w problemach, w których trudno
na pierwszy rzut oka wyłapać UB (UB czasu wykonania) a także dla mniej
doświadczonych programistów, którzy nie potrafią zidentyfikować UB
czasu kompilacji.

Rozwiązanie jest bardzo proste: polega na tym aby liczyć epokę dla
kolejnego punktu charakterystycznego i z pewnym małym dodatkowym
narzutem pamięci dla każdej liczby sprawdzać, czy zmienna nie została
już zmodyfikowana w aktualnej epoce. Jak łatwo zauważyć bez problemu
można napisać klasę Number, która będzie zabezpieczona przed UB czasu
kompilacji (to znaczy takim, który kompilator bez trudu mógłby
zidentyfikować w czasie kompilacji; ale będzie przed nim zabezpieczona
dopiero w czasie wykonania) i czasu wykonania (którego kompilator nie
mógłby wykryć w czasie kompilacji) i używać jej w podany poniżej
sposób. Klasa ta może być wykorzystana między innymi do testowania w
czasie wykonania, czy wyrażenie jest UB czasu kompilacji, albo i
przede wszystkim do sprawdzania, czy wyrażenie zawiera UB czasu
wykonania, które może być bardzo trudne do wykrycia innymi metodami.
Jeśliby się okazało, że klasa Number wykryła UB czasu wykonania trzeba
zbudować wyrażenia w taki sposób, aby nie dopuszczało takiej
możliwości, co nie jest takie trudne w realizacji. Wystarczy
rozdzielić punktami charakterystycznymi np. średnikiem, wszystkie
preoperacje i postoperacje oraz przypisania rozbijając złożone
wyrażenie na wiele mniejszych.

typedef unsigned short int age_type; // it givees up to 2^16
simultaneous modyfications beetwen two consecutive sequence points
(calls of Number::SP())
typedef Number<0, int, age_type> Int;
typedef Number<0, float, age_type> Float;

Int a = 5;

Int::SP(); // sequence point
Int result = ((++a) + (a));

Int::SP();
Int result2 = ((a) + (--a));

try {
Int::SP(); // always before the expression (not after) to avoid loss
of actualization of sequence point age in case of UB() exception
Int result = ((++a) + (++a)); /* throws Int::UB() */
} catch (const Int::UB&) {
std::cerr << "expression 1 is UB";
}

Jedyny błąd może wyniknąć z powodu przepełnienia wartości agei/lub
global_age, jednak problem powstanie tylko jeśli zmienna dla danej
klasy Intów będzie używana, a potem przez dokładnie 2 ^ n (n - liczba
bitów w typie age_type) wystąpień punktów charakterystycznych (na
przykład dla wyrażenia obliczanego w pętli) nie będzie używana, i
znowu zostanie użyta, czyli powinien być to przypadek bardzo rzadki, i
to jest koszt tego najszybszego jak mi się zdaje rozwiązania tego
problemu. Łatwo można jednak temu zapobiec wykonując przed wyrażeniem,
ale nie w nim, dla danej zmiennej funkcję reset() lub operator ()
{e.g.: a()} lub inicjując go nową wartością (e.g. a = 3), a nawet
wykonując: a=a. Dla pewności można to zrobić dla wszystkich użytych
zmiennych. Jeśli wszystkie zmienne są resetowane przed wyrażeniem,
które na nich operuje, to typem age_type może być bool (czyli
teoretycznie wystarczy poświęcić jeden bit, w dostarczonej przeze mnie
implementacji jest to jeden bajt).

Implementacja klasy Number wykrywającej UB czasu kompilacji i czasu
wykonania. Żeby uprościć kod klasa implementuje tylko dwie funkcje
modyfikujące zmienną: "++" oraz "+=", gdyż wzorując się na nichmożna
napisać wszystkie inne. Klasa także jako ciekawostkę implementuje
operator potęgowania a**b.

#include <math.h>

namespace utilities {
template <class A, class B> struct wider_type;
template <class A> struct wider_type<A, A> { typedef A result; };
template <> struct wider_type<float, int> { typedef float result; };
template <> struct wider_type<double, int> { typedef double
result; };
template <> struct wider_type<float, unsigned int> { typedef float
result; };
template <> struct wider_type<double, unsigned int> { typedef double
result; };
template <> struct wider_type<int, float> { typedef float result; };
template <> struct wider_type<int, double> { typedef double
result; };
template <> struct wider_type<unsigned int, float> { typedef float
result; };
template <> struct wider_type<unsigned int, double> { typedef double
result; };
}

template <class number_type>
struct PowerNumber {
typedef PowerNumber alias;
number_type value;

alias() {}
alias(const number_type value) : value(value) {}

template <class number_type1>
static typename utilities::wider_type<number_type1,
number_type>::result
power(number_type n, number_type1 a) { return expf(n * logf(a)); }

friend number_type
operator * (const number_type& lhs, const alias& rhs)
{ return power(rhs.value, lhs); }

template <class number_type1>
friend typename utilities::wider_type<number_type1,
number_type>::result
operator * (const number_type1& lhs, const alias& rhs)
{ return power(rhs.value, lhs); }

};

struct AllUB {};

template<
const unsigned int expr_enum = 0,
class number_type = int, // floating or integer
class age_type = unsigned int>
class Number {
public:
typedef Number alias;
typedef number_type number_type;
typedef age_type age_type;
struct UB : public AllUB {};

private:
static age_type global_age;
number_type value;
age_type age;

public:
alias() : age(global_age - 1) {}
alias(const number_type value) : age(global_age - 1), value(value) {}

alias& operator =(const alias& rhs)
{
if (&rhs != this /*&& rhs.value != rhs.value*/) /* optimization when
values are the same, not modification and not UB, but this is not the
most frequent behaviour, so is out of code */
{
test_UB();
value = rhs.value;
exit();
}
else
age = global_age - 1;
return *this;
}

operator number_type&() { return value; }
operator const number_type&() const { return value; }

void test_UB() { if (age == global_age) throw UB(); }
void exit() { age = global_age; }

// example of modifying operation
alias& operator ++()
{ test_UB();
++value;
return exit(), *this;
}

alias& operator +=(const number_type rhs)
{ test_UB();
value += rhs;
return exit(), *this;
}

void reset() { age = global_age - 1; }
alias& operator ()() { reset(); return *this; }
static void SP() { ++global_age; }

// example of realization power function with ** operator
const PowerNumber<number_type> operator *() const { return value; }
};

template<const unsigned int expr_enum, class number_type, class
age_type>
age_type Number<expr_enum, number_type, age_type>::global_age = 1;

void
test_Int_UB()
{
typedef unsigned short int age_type; // it givees up to 2^16
simultaneous modyfications beetwen two consecutive sequence points
(calls of Number::SP())
typedef Number<0, int, age_type> Int;
typedef Number<0, float, age_type> Float;

Int a = 5;

Int::SP(); // sequence point
Int result = ((++a) + (a));

Int::SP();
Int result2 = ((a) + (--a));

Int::SP(); Float::SP();
Float b = 1.0 / 3.0;
Float result3 = ((a = 5, /* <- here is a sequence point */ Int::SP(),
a = 8) ** b); // "**" - power operator

try {
Int::SP(); // always before the expression (not after) to avoid loss
of actualization of sequence point age in case of UB() exception
Int result = ((++a) + (++a)); /* throws Int::UB(); */
} catch (const Int::UB&) {
std::cerr << "expression 1 is UB";
}
try {
Int b = 0;
Float c = 0;
for (age_type i = 0; i != age_type(-1); ++i)
{ // test na przepełnienie typu epoki
Int::SP(), Float::SP(); // always before the expression (not after)
to avoid loss of actualization of sequence point age in case of UB()
exception
b(), c();// reset 'b' and 'c'
float result = ((b += Int(1.0)) + (c += Float(2))); // thanks to
reseting 'b' with call of b() it is safe and won't throw Int::UB() by
accident;
/* Float(2) (could be also Float::number_type(2)) is used instead
of just 2 or 2.0 to avoid other overloads of operator "+=", general
rule is: when UB sensitive operator "+=EXPR" is used and type of EXPR
doesn't equal Number::number_type, you must use it like this:
UB_sensitive_variable += type_of_UB_sensitive_variable(EXPR) or
UB_sensitive_variable +=
type_of_UB_sensitive_variable::number_type(EXPR), which is faster */
}
} catch (const AllUB& ) {
std::cerr << "expression 2 is UB";
}
}
Witold Kuzminski (01.10.2010, 18:37)
On Oct 1, 2:11 am, Zbigniew Płotnicki
<zbigniewplotnicki> wrote:

> chciałbym
> zwrócić głównie uwagę na moją klasę chroniącą przed UB czasu
> kompilacji i czasu wykonania


Twoja klasa nie moze "chronic przed UB", bo twoje dotychczasowe
posty wskazuja, ze nie rozumiesz kiedy UB wystepuje.

> Rozwiązanie jest bardzo proste: polega na tym aby liczyć epokę dla


Nie. Rozwiazanie jest duzo prostrze.
Nie nalezy pisac wyrazen na tyle zlozonych, ze nie rozumie sie sposobu
ich evaluacji.
Zbigniew Płotnicki (01.10.2010, 18:49)
On 1 Paź, 18:37, Witold Kuzminski <witoldkuzmin> wrote:
> On Oct 1, 2:11 am, Zbigniew Płotnicki
> <zbigniewplotnicki> wrote:
> Twoja klasa nie moze "chronic przed UB", bo twoje dotychczasowe
> posty wskazuja, ze nie rozumiesz kiedy UB wystepuje.
> Nie. Rozwiazanie jest duzo prostrze.
> Nie nalezy pisac wyrazen na tyle zlozonych, ze nie rozumie sie sposobu
> ich evaluacji.


No i kolejny bzdet. Proszę nie darzcie mnie nimi.
Zbigniew Płotnicki (01.10.2010, 18:50)
On 1 Paź, 18:49, Zbigniew Płotnicki <zbigniewplotnicki>
wrote:
> On 1 Paź, 18:37, Witold Kuzminski <witoldkuzmin> wrote:
> No i kolejny bzdet. Proszę nie darzcie mnie nimi.


Klasa naprawdę chroni przed UB. Nikt nie jest zainteresowany?
Zbigniew Płotnicki (01.10.2010, 18:51)
On 1 Paź, 18:50, Zbigniew Płotnicki <zbigniewplotnicki>
wrote:
> On 1 Paź, 18:49, Zbigniew Płotnicki <zbigniewplotnicki>
> wrote:
> Klasa naprawdę chroni przed UB. Nikt nie jest zainteresowany?


Witold, bądź chłopem, także nie wiedziałeś że to UB, bo pisałeś coś o
kalesonach cioci na ten temat.
Zbigniew Płotnicki (01.10.2010, 18:53)
On 1 Paź, 18:51, Zbigniew Płotnicki <zbigniewplotnicki>
wrote:
> On 1 Paź, 18:50, Zbigniew Płotnicki <zbigniewplotnicki>
> wrote:
> Witold, bądź chłopem, także nie wiedziałeś że to UB, bo pisałeś coś o
> kalesonach cioci na ten temat.


Zresztą, grupowicze, powiedzcie sobie prawdę w oczy, tylko Jacek
wiedział, za co należą mu się brawa.
Zbigniew Płotnicki (01.10.2010, 19:13)
Przypuscmy ze ktos przez zamyślenie napisał kod UB, projekt jest tak
wielki, że już go nie obejmuje, a ma termin na wczoraj, jednak wciąż
gdzieś jest błąd w obliczeniach, teraz pytanie otwarte: sięgnąćpo tę
klasę czy nie ?
mvoicem (01.10.2010, 19:45)
(10/01/2010 06:53 PM), Zbigniew Płotnicki wrote:
> On 1 Paź, 18:51, Zbigniew Płotnicki<zbigniewplotnicki>
> wrote:
>> On 1 Paź, 18:50, Zbigniew Płotnicki<zbigniewplotnicki>
>>> On 1 Paź, 18:49, Zbigniew Płotnicki<zbigniewplotnicki> [...]


Duża to chyba frajda gadać tam samemu ze sobą, co? :). Nie rozważałeś
może opcji żeby sobie pogadać gdzie indziej niż na tej grupie?

p. m.
Zbigniew Płotnicki (01.10.2010, 22:12)
On 1 Paź, 19:45, mvoicem <mvoi> wrote:
> (10/01/2010 06:53 PM), Zbigniew Płotnicki wrote:> On 1 Paź, 18:51, Zbigniew Płotnicki<zbigniewplotnicki>
> [...]
> Duża to chyba frajda gadać tam samemu ze sobą, co? :). Nie rozważałeś
> może opcji żeby sobie pogadać gdzie indziej niż na tej grupie?
> p. m.


Nie jest to zbyt duża frajda, ale lepsze to niżby mieli odpisywać
głupoty znowu. Niech się dobrze namyślą, to im dobrze zrobi.
Zbigniew Płotnicki (01.10.2010, 22:14)
On 1 Paź, 22:12, Zbigniew Płotnicki <zbigniewplotnicki>
wrote:
> On 1 Paź, 19:45, mvoicem <mvoi> wrote:
> Nie jest to zbyt duża frajda, ale lepsze to niżby mieli odpisywać
> głupoty znowu. Niech się dobrze namyślą, to im dobrze zrobi.


Zresztą nigdy to strata czasu pogadać z inteligentnym rozmówcą,
dlatego zapraszam do rozmowy.
Zbigniew Płotnicki (01.10.2010, 22:22)
On 1 Paź, 22:14, Zbigniew Płotnicki <zbigniewplotnicki>
wrote:
> On 1 Paź, 22:12, Zbigniew Płotnicki <zbigniewplotnicki>
> wrote:
> Zresztą nigdy to strata czasu pogadać z inteligentnym rozmówcą,
> dlatego zapraszam do rozmowy.


Nie wiem czy Wy macie jakieś kompleksy? Tak żywo Wam szla rozmowa na
temat wad jednolinikowego zapisu, a tu jesli chodzi o rzecz duzo
ciekawszą, wycofujecie sie w niebyt?

Porażka.
Michoo (01.10.2010, 22:50)
W dniu 01.10.2010 22:22, Zbigniew Płotnicki pisze:
> Nie wiem czy Wy macie jakieś kompleksy? Tak żywo Wam szla rozmowa na
> temat wad jednolinikowego zapisu, a tu jesli chodzi o rzecz duzo
> ciekawszą, wycofujecie sie w niebyt?

Jednolinijkowiec rzuca się w oczy. Przeczytanie i przetrawienie
wiadomości z początku tego wątku wymaga poświęcenia chwili czasu i
wbicia się w "odpowiedni tok myślenia". Spróbuję znowu po piwie.
Zbigniew Płotnicki (01.10.2010, 23:26)
On 1 Paź, 22:50, Michoo <michoo_n> wrote:
> W dniu 01.10.2010 22:22, Zbigniew Płotnicki pisze:> Nie wiem czy Wy macie jakieś kompleksy? Tak żywo Wam szla rozmowa na
> Jednolinijkowiec rzuca się w oczy. Przeczytanie i przetrawienie
> wiadomości z początku tego wątku wymaga poświęcenia chwili czasu i
> wbicia się w "odpowiedni tok myślenia". Spróbuję znowu po piwie.
> --
> Pozdrawiam
> Michoo


Generalnie skłaniam się ku temu, że łatwiej zrozumieć na czym polega
ten rodzaj UB niż zastosować tę klasę. Ale czekam na waszą opinię. Na
samej klasie nieszczególnie mi zależy, bardziej interesowało mnie
zdanie programistów czy może się na coś przydać.
Michoo (02.10.2010, 00:16)
W dniu 01.10.2010 23:26, Zbigniew Płotnicki pisze:
> Generalnie skłaniam się ku temu, że łatwiej zrozumieć na czym polega
> ten rodzaj UB niż zastosować tę klasę. Ale czekam na waszą opinię. Na
> samej klasie nieszczególnie mi zależy, bardziej interesowało mnie
> zdanie programistów czy może się na coś przydać.

Ok, przebrnąłem.
Powtórzę po raz n+1:
Jeżeli ktoś odczuwa wewnętrzny przymus pisania kodu nazwijmy to
"ryzykownego" to ta klasa będzie prawdoodobnie dla niego bardzo
przydatna. Ja zamiast jej stosowania i debugowania kodu, wolę pisać od
początku kod, który nie ma prawa nie działać w myśl zasady "mniej
ryzykownych konstrukcji -> mniej potencjalnych błędów -> mniej
niewykrytych na etapie pisania błędów -> mniej błędów w ogóle".
Witold Kuzminski (02.10.2010, 06:17)
On Oct 1, 6:16 pm, Michoo <michoo_n> wrote:

> Jeżeli ktoś odczuwa wewnętrzny przymus pisania kodu nazwijmy to
> "ryzykownego" to ta klasa będzie prawdoodobnie dla niego bardzo
> przydatna.


Chyba wypiles za duzo piwa.

Podobne wątki