Преобразование типов.
От: dr. Acula Украина  
Дата: 22.11.15 13:21
Оценка:
Коллега с 20+ опыта породил следующий код.

//в системных хедерах
typedef unsigned short WORD;

//свой код
typedef std::map<std::string, WORD> Values;

struct Less {
    bool operator()(Values::value_type const& left
    , Values::value_type const& right) const {
       if (right.second == TEMPERATURE_UNKNOWN 
          || left.second == TEMPERATURE_UNKNOWN) {
             return false;
       }
       short const signed_left = *reinterpret_cast<short const*>(&left.second);
       short const signed_right = *reinterpret_cast<short const*>(&right.second);
       bool const result = signed_left < signed_right;
       return result;
    }
};


Да, я в курсе, что подобный функтор использовать в качестве предиката в контейнерах нельзя, т.к. он не обеспечивает strict weak ordering.

упрощенно без кастов здесь

Но даже больше меня тревожит ход мыслей, который привёл к конструкции:
short const signed_left = *reinterpret_cast<short const*>(&left.second);


1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?
Возможно, я что-то упускаю?
Простой C-style или static_cast недостаточны для подобной конвертации?


2. Код теоретически кроссплатформенный (x86 и ARM), в реальности — только ARM-
В этом может быть загвоздка?
Re: Преобразование типов.
От: VTT http://vtt.to
Дата: 22.11.15 14:31
Оценка:
Этот компаратор используется в качестве предиката алгоритма, а не контейнера, и к нему требование strict weak ordering вроде не предъявляется.

Зачем вообще что-то кастовать, а не хранить сразу signed short — совершенно непонятно.
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[2]: Преобразование типов.
От: dr. Acula Украина  
Дата: 22.11.15 16:18
Оценка:
VTT>Этот компаратор используется в качестве предиката алгоритма, а не контейнера, и к нему требование strict weak ordering вроде не предъявляется.
Отлично, но, например, max_element/min_element из std с ним выдают белиберду.
  Скрытый текст
#include <iostream>

#include <algorithm>

#include <vector>

#include <map>

 

unsigned short INVALID = 0x7FFF;

 

struct less

{

    bool operator() (std::map<int, unsigned short>::value_type const& a, std::map<int, unsigned short>::value_type const& b)

    {

        if (a.second == INVALID || b.second == INVALID)

            return false;

        return a.second < b.second;

    }

};

 

int main()

{

        std::map<int, unsigned short> a;

        a[1] = INVALID;

        a[2] = 1;

        a[3] = 2;

        a[4] = 3;

    std::cout << "A max = " << std::max_element(a.begin(), a.end(), less())->second << std::endl;

    std::cout << "A min = " << std::min_element(a.begin(), a.end(), less())->second<< std::endl;

 

    std::map<int, unsigned short> b;

        b[1] = 1;

        b[2] = 2;

        b[3] = 3;

        b[4] = INVALID;

    std::cout << "B max = " << std::max_element(b.begin(), b.end(), less())->second << std::endl;

    std::cout << "B min = " << std::min_element(b.begin(), b.end(), less())->second << std::endl;

 

 

    return 0;

}

A max = 32767
A min = 32767
B max = 3
B min = 1


Базируясь на стандарте :
14882:2014

25.4 Sorting and related operations
...
3 For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i,
*j) != false defaults to *i < *j != false. For algorithms other than those described in 25.4.3 to work
correctly, comp has to induce a strict weak ordering on the values.
4 The term strict refers to the requirement of an irreflexive relation (!comp(x, x) for all x), and the term weak
to requirements that are not as strong as those for a total ordering, but stronger than those for a partial
ordering. If we define equiv(a, b) as !comp(a, b) && !comp(b, a), then the requirements are that comp
and equiv both be transitive relations:
(4.1) — comp(a, b) && comp(b, c) implies comp(a, c)
(4.2) — equiv(a, b) && equiv(b, c) implies equiv(a, c) [Note: Under these conditions, it can be shown
that
(4.2.1) — equiv is an equivalence relation
(4.2.2) — comp induces a well-defined relation on the equivalence classes determined by equiv
(4.2.3) — The induced relation is a strict total ordering. —end note


VTT>Зачем вообще что-то кастовать, а не хранить сразу signed short — совершенно непонятно.

Хранить сразу нельзя.
Извне приходит unsigned.
Зачем делать через указатель я не понимаю.
Отредактировано 22.11.2015 16:28 dr. Acula . Предыдущая версия . Еще …
Отредактировано 22.11.2015 16:28 dr. Acula . Предыдущая версия .
Re: Преобразование типов.
От: smeeld  
Дата: 22.11.15 19:30
Оценка: -4 :))) :))
Здравствуйте, dr. Acula, Вы писали:

DA> short const signed_left = *reinterpret_cast<short const*>(&left.second);



Заменить эти Страусовые понтовые заклинания типа reinterpret_cast на

short const signed_left = *(short const*)(&left.second);


и станет всё понятно и просто.
Re: Преобразование типов.
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 22.11.15 21:45
Оценка:
Здравствуйте, dr. Acula, Вы писали:

DA>Но даже больше меня тревожит ход мыслей, который привёл к конструкции:

DA>
DA>short const signed_left = *reinterpret_cast<short const*>(&left.second);
DA>


DA>1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?

DA> Возможно, я что-то упускаю?
DA> Простой C-style или static_cast недостаточны для подобной конвертации?

Реальной необходимости нет. Ход мыслей... Например, до этого писался код, где reinterpret_cast использовался для структур... Или просто померещилось что-то...
Re: Преобразование типов.
От: Vain Россия google.ru
Дата: 22.11.15 23:48
Оценка:
Здравствуйте, dr. Acula, Вы писали:

DA> Простой C-style или static_cast недостаточны для подобной конвертации?

static_cast проверяет возможность приведения и может не скомпилироваться. c-style будет приведён к одному из возможных приведений в c++ — const_cast, static_cast, reinterpret_cast. чел сразу явно написал к чему он хочет привести, это и есть с++ стиль.

DA>2. Код теоретически кроссплатформенный (x86 и ARM), в реальности — только ARM-

DA> В этом может быть загвоздка?
код вообще может выдать шлак, если там в первых short const байтах объекта шлак.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re: Преобразование типов.
От: andyp  
Дата: 23.11.15 00:05
Оценка: +1
Здравствуйте, dr. Acula, Вы писали:

DA>Коллега с 20+ опыта породил следующий код.


Ордеринг поправь. Не делается сравнение TEMP_UNKNOWN c валидными значениями, вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.

Если охота, чтобы невалидные элементы были наименьшими:

    bool operator() (std::map<int, unsigned short>::value_type const& a, std::map<int, unsigned short>::value_type const& b)
    {
        if ((a.second == INVALID) && (b.second == INVALID))
            return false;
        if(a.second == INVALID)  
                return true;
        if(b.second == INVALID)  
                return false;            
        return a.second < b.second;
    }
Re: Преобразование типов.
От: CEMb  
Дата: 23.11.15 04:47
Оценка:
Здравствуйте, dr. Acula, Вы писали:

DA>1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?

DA> Возможно, я что-то упускаю?
DA> Простой C-style или static_cast недостаточны для подобной конвертации?

я может чего не понял, но обе конструкции в 50% случаев дают ошибочный результат же?
более того, такой reinterpret_cast с нормальным int-ом вообще ошибочен
Re: Преобразование типов.
От: uzhas Ниоткуда  
Дата: 23.11.15 08:05
Оценка:
Здравствуйте, dr. Acula, Вы писали:

DA>Да, я в курсе, что подобный функтор использовать в качестве предиката в контейнерах нельзя, т.к. он не обеспечивает strict weak ordering.

это уже существенный аргумент, чтобы переработать код

DA>Но даже больше меня тревожит ход мыслей, который привёл к конструкции:

DA>
DA>short const signed_left = *reinterpret_cast<short const*>(&left.second);
DA>

действительно, странный код. предположу, что формально — это UB и этот код не равносилен коду
short const signed_left = static_cast<short>(left.second);


DA>1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?

DA> Возможно, я что-то упускаю?
зависит от того, как следует интерпретировать числа типа WORD.
в простейшем случае никаких кастов делать вообще не надо

DA>2. Код теоретически кроссплатформенный (x86 и ARM), в реальности — только ARM-

DA> В этом может быть загвоздка?
x86 — little endian
ARM — big/little (bi-) endian
эти нюансы надо держать в голове
Re[2]: Преобразование типов.
От: _hum_ Беларусь  
Дата: 23.11.15 14:35
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, dr. Acula, Вы писали:


DA>>Коллега с 20+ опыта породил следующий код.


A>Не делается сравнение TEMP_UNKNOWN c валидными значениями,


а можно поинтересоваться, почему?


A>вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.


в каком смысле развалилась? в логическом смысле она в исходном варианте сравнения вроде как выполнена (а нарушена транзитивность по несравнимости)
Re[3]: Преобразование типов.
От: andyp  
Дата: 23.11.15 14:58
Оценка:
Здравствуйте, _hum_, Вы писали:

A>>Не делается сравнение TEMP_UNKNOWN c валидными значениями,


__>а можно поинтересоваться, почему?


Да так уж написано было первом посте темы

A>>вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.


__>в каком смысле развалилась? в логическом смысле она в исходном варианте сравнения вроде как выполнена (а нарушена транзитивность по несравнимости)


В смысле f(invalid, valid) == (!f(valid, invalid))

должно выполняться (см. аксиома 2, https://www.sgi.com/tech/stl/StrictWeakOrdering.html)
Re[4]: Преобразование типов.
От: _hum_ Беларусь  
Дата: 23.11.15 15:26
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, _hum_, Вы писали:


A>>>Не делается сравнение TEMP_UNKNOWN c валидными значениями,


__>>а можно поинтересоваться, почему?


A>Да так уж написано было первом посте темы


что именно?

A>>>вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.


__>>в каком смысле развалилась? в логическом смысле она в исходном варианте сравнения вроде как выполнена (а нарушена транзитивность по несравнимости)


A>В смысле f(invalid, valid) == (!f(valid, invalid))


A>должно выполняться (см. аксиома 2, https://www.sgi.com/tech/stl/StrictWeakOrdering.html)


там написано

Antisymmetry f(x, y) implies !f(y, x)

что, как я понимаю, означает следование в виде импликации: "когда f(x, y) == true, то !f(y, x) == true" . иными словами, никто не запрещает ситуацию, когда f(x, y) == false и f(y, x) == false.
Re[5]: Преобразование типов.
От: andyp  
Дата: 23.11.15 15:55
Оценка: +1
Здравствуйте, _hum_, Вы писали:

__>Здравствуйте, andyp, Вы писали:


__>что именно?


код предиката в стартовом посте.

__>там написано

__>

__>Antisymmetry f(x, y) implies !f(y, x)

__>что, как я понимаю, означает следование в виде импликации: "когда f(x, y) == true, то !f(y, x) == true" . иными словами, никто не запрещает ситуацию, когда f(x, y) == false и f(y, x) == false.

Implies переводится как предполагает. f(x, y) == false и f(y, x) == false для less бывает только когда x равно y. Если ты посмотришь на то, что писал я в педыдущем посте, то там присутствуют заведомо неравные valid (известная температура) и invalid (неизвестная температура). Именно это и нарушалось в коде предиката, который привел ТС:
less(unknown,100) == false, less(100,unknown) == false => 100 == unknown (WTF ???) и поиск минимума-максимума стал невозможен.

PS Вика приводит более внятный вид аксиомы антисимметрии, но для отношения меньше-равно:
if a ≤ b and b ≤ a, then a = b
https://en.wikipedia.org/wiki/Total_order
Отредактировано 23.11.2015 16:02 andyp . Предыдущая версия .
Re[3]: Преобразование типов.
От: dr. Acula Украина  
Дата: 23.11.15 16:05
Оценка:
A>>Не делается сравнение TEMP_UNKNOWN c валидными значениями,

__>а можно поинтересоваться, почему?

такое требоваие спецификации.


A>>вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.

__>в каком смысле развалилась? в логическом смысле она в исходном варианте сравнения вроде как выполнена (а нарушена транзитивность по несравнимости)
нет, нарушена антисимметричность.
Re: Преобразование типов.
От: B0FEE664  
Дата: 23.11.15 16:30
Оценка: 2 (1)
Здравствуйте, dr. Acula, Вы писали:

DA>Но даже больше меня тревожит ход мыслей, который привёл к конструкции:

DA>
DA>short const signed_left = *reinterpret_cast<short const*>(&left.second);
DA>


DA>1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?

DA> Возможно, я что-то упускаю?
DA> Простой C-style или static_cast недостаточны для подобной конвертации?

Так как вопрос про "ход мыслей", то придётся использовать телепатические способности и прочее "лечение по фотографии". Как следствие — никаких гарантий.
Смотрите: left.second типа unsigned short, а мы хотим его в signed short перевести. Зачем — я не знаю, но думаю, что имеет место передача знаковых значений через беззнаковое. (Я бы предположил наличие в программе передачи сообщений или какую-нибудь коммуникацию и построение точно такого же map'а, как на другом конце) Нельзя просто так перевести unsigned short в signed short потому, что для положительных значений выходящих за пределы представления signed short результат то ли неспецифицирован, то ли implemented defined. Но! Если мы знаем, что представление чисел организовано в дополнительном коде (а это скорее всего так), то мы можем просто интерпретировать кусок памяти, как знаковое число, поэтому reinterpret_cast через указатели. Через указатели, потому что "никто" не помнит, можно ли делать прямой reinterpret_cast<short>(..). (Может и можно — RTFM, но лень — матушка).

DA>2. Код теоретически кроссплатформенный (x86 и ARM), в реальности — только ARM-

DA> В этом может быть загвоздка?

Теоретически, этот код не кроссплатформенный, но на практике, почти везде будет работать.
В любом случае это плохой стиль и, минимум, необходим static_assert(sizeof(Values::value_type) == sizeof(short), "wrong implementation");
И каждый день — без права на ошибку...
Re[6]: Преобразование типов.
От: _hum_ Беларусь  
Дата: 23.11.15 16:34
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, _hum_, Вы писали:


__>>Здравствуйте, andyp, Вы писали:


__>>что именно?


A>код предиката в стартовом посте.


__>>там написано

__>>

__>>Antisymmetry f(x, y) implies !f(y, x)

__>>что, как я понимаю, означает следование в виде импликации: "когда f(x, y) == true, то !f(y, x) == true" . иными словами, никто не запрещает ситуацию, когда f(x, y) == false и f(y, x) == false.

A>Implies переводится как предполагает. f(x, y) == false и f(y, x) == false для less бывает только когда x равно y. Если ты посмотришь на то, что писал я в педыдущем посте, то там присутствуют заведомо неравные valid (известная температура) и invalid (неизвестная температура). Именно это и нарушалось в коде предиката, который привел ТС:

A>less(unknown,100) == false, less(100,unknown) == false => 100 == unknown (WTF ???) и поиск минимума-максимума стал невозможен.

A>PS Вика приводит более внятный вид аксиомы антисимметрии, но для отношения меньше-равно:

A>if a ≤ b and b ≤ a, then a = b
A>https://en.wikipedia.org/wiki/Total_order

это не совесм то. это антисимметричность, а надо асимметричность ака Asymmetric_relation

In mathematics an asymmetric relation is a binary relation on a set X where:

For all a and b in X, if a is related to b, then b is not related to a.[1]


помимо вариантов a < b, b < a есть еще вариант "a с b несравнимы", то есть, не выполняется ни "a < b", ни "b < a".
асимметричность говорит, что если элементы сравнимы, то выполняться должен только один вариант сравнения.
Re[4]: Преобразование типов.
От: _hum_ Беларусь  
Дата: 23.11.15 16:35
Оценка:
Здравствуйте, dr. Acula, Вы писали:


A>>>Не делается сравнение TEMP_UNKNOWN c валидными значениями,


__>>а можно поинтересоваться, почему?

DA>такое требоваие спецификации.

чьей? вашей, или сишной (что максимальные по значению числа типов нельзя сравнивать)?

A>>>вот аксиома антисимметрии и развалилась. Остальное вроде бы вычурно, но безобидно.

__>>в каком смысле развалилась? в логическом смысле она в исходном варианте сравнения вроде как выполнена (а нарушена транзитивность по несравнимости)
DA>нет, нарушена антисимметричность.

нет. см. выше
Re: Преобразование типов.
От: AndrewJD США  
Дата: 23.11.15 17:49
Оценка:
Здравствуйте, dr. Acula, Вы писали:

DA>Но даже больше меня тревожит ход мыслей, который привёл к конструкции:

DA>
DA>short const signed_left = *reinterpret_cast<short const*>(&left.second);
DA>


DA>1. Есть ли реальная необходимость делать касте через указатель и его последующее разыменовывание?

DA> Возможно, я что-то упускаю?

DA>2. Код теоретически кроссплатформенный (x86 и ARM), в реальности — только ARM-


И что GCC не ругается на такой код варнингами в стиле “dereferencing type-punned pointer will break strict aliasing”?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[2]: Преобразование типов.
От: sokel Россия  
Дата: 24.11.15 00:41
Оценка: 1 (1) +2
Здравствуйте, andyp, Вы писали:

A>Если охота, чтобы невалидные элементы были наименьшими:

bool operator() (optional a, optional b) const {
    bool a_null = is_null(a);
    bool b_null = is_null(b);
    if(a_null != b_null) return a_null; // или b_null чтобы в конец
    return !a_null && value(a) < value(b);
}

Но здесь INVALID и так с краю, он же numeric_limits<short>::max(), можно тупо сравнивать, — какая разница, слева он или справа.
Только это же не поиск, судя по использованию min_element/max_element, добавление транзитивности компаратору ничего не даст.
Рискну предположить что тут не order нужен, а фильтрация INVALID элементов.
struct min_max_temperature {
    void operator()(const pair<int, short>& t) {
        if(t.second == INVALID)
            return;
        if(min == INVALID || min > t.second))
            min = t.second;
        if(max == INVALID || max < t.second))
            max = t.second;
    }
    short min = INVALID;
    short max = INVALID;
};
auto min_max = std::foreach(temperatures.begin(), temperatures.end(), min_max_temperature());

Ну или filter_iterator какой нибудь.
Re[3]: Преобразование типов.
От: sokel Россия  
Дата: 24.11.15 01:05
Оценка:
S>Здравствуйте, andyp, Вы писали:

A>>Если охота, чтобы невалидные элементы были наименьшими:


Кстати, довольно удобно с null значениями работать через enum'ы:

using yes_type = char;
struct no_type { char padding[8]; };

template<typename T>
struct is_nullable_enum {
    template<typename U, U u = U::null> static yes_type test(U*);
    template<typename U> static no_type test(...);
    const static bool value = std::is_enum<T>::value && sizeof(test<T>(nullptr)) == sizeof(yes_type);
};

template<class T>
typename std::enable_if<is_nullable_enum<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T value) {
    if(value == T::null) os << "null";
    else os << static_cast<std::underlying_type<T>::type>(value);
    return os;
}

enum class temperature_t : short { null = 0x7fff };

int main() {
    temperature_t t = temperature_t::null;
    std::cout << t << std::endl; // --> null
    t = temperature_t(0);
    std::cout << t << std::endl; // --> 0

    return 0;
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.