Re[5]: Alias cast
От: Masterkent  
Дата: 21.03.09 20:07
Оценка: 5 (1) +1
Здравствуйте, Alexander G, Вы писали:

AG>Чтобы делать безопасные reinterpret_cast'ы с последующим доступом через результат каста.


Ну, давайте посмотрим на перечень этих самых "безопасных" преобразований из U* в T* (будем считать, что со ссылками дела обстоят аналогично).

1) T и U — типы, которые, возможно, отличаются лишь cv-квалификацией. Очень ценное применение reinterpret_cast
2) T — это char или unsigned char (странно, что в 3.10/15 отсутствует signed char — IMHO, баг стандарта)
3) T и U — интегральные типы, различающиеся в знаковости.

Довольно скромный перечень, однако. Для последнего вида преобразований я предпочитаю использовать отдельную функцию signed_cast. Во-первых, код более понятен, во-вторых, пресекается возможность нежелательных преобразований вроде wchar_t* -> unsigned char* (вместо char* -> unsigned char*, как задумывалось) или unsigned char* -> wchar_t. alias_pointer_cast в данном случае не пресекает часть очевидно нежелательных преобразований и с точки зрения самодокументируемости код практически не улучшает.

Что происходит, когда какой-то из типов оказывается bool или wchar_t, — это отдельная история

Итак, из потенциально полезных остаётся преобразование (2). Что же проще запомнить: что reinterpret cast в указатель на char / signed char / unsigned char действительно "безопасен" или что alias_pointer_cast — это разновидность "безопасного" reinterpret cast?

Конечно, тут можно возразить, что эта конструкция может использовать в каком-нибудь шаблоне, где заранее может быть неизвестно, какой вид преобразования (1-й, 2-й или 3-й) потребуется. Интересно, как часто возникает такая ситуация?
Alias cast
От: Alexander G Украина  
Дата: 21.03.09 07:48
Оценка: 6 (1)
Никого не посещала такая идея насчёт проверки strict aliasing ?

#include <boost/type_traits.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/logical.hpp>

namespace detail
{
    template<typename T>
    struct erase_cv_and_sign
    {
        typedef typename boost::remove_cv<
            typename boost::mpl::eval_if<
                boost::is_integral<T>,
                boost::make_unsigned<T>,
                boost::mpl::identity<T>
            >::type
        >::type type;
    };

    template<typename T, typename U>
    struct is_correct_alias
    {
        typedef typename erase_cv_and_sign<T>::type CT;
        typedef typename erase_cv_and_sign<U>::type CU;
        static const bool value = typename boost::mpl::or_<
            boost::is_same<CT, CU>,
            boost::is_same<char, CT>,
            boost::is_same<char unsigned, CT>,
            boost::is_base_of<CT, CU>
        >::value;
    };

    template<typename U>
    struct not_correct_alias
    {
        typedef U type;
    };

    template<bool Correct>
    struct check
    {
        template<typename T, typename U>
        static T* do_cast(U*)
        {
            return not_correct_alias<U*>();
        }
    };

    template<>
    struct check<true>
    {
        template<typename T, typename U>
        static T* do_cast(U* u)
        {
            return reinterpret_cast<T*>(u);
        }
    };

}

template<typename T, typename U>
T* alias_pointer_cast(U* u)
{
    return detail::check<
        detail::is_correct_alias<T, U>::value
    >::template do_cast<T, U>(u);
}

template<typename T, typename U>
T& alias_reference_cast(U& u)
{
    return *alias_pointer_cast<T, U>(&u);
}

int _tmain(int argc, _TCHAR* argv[])
{
    unsigned int x = 5;
    
    int * y = alias_pointer_cast<int>(&x);
    /* ok */

    char * z = alias_pointer_cast<char>(&x);
    /* ok */

    float * w = alias_pointer_cast<float>(&x);
    /* error C2440: 'return' : cannot convert from 
      'detail::not_correct_alias<U>' to 'float *'
        with [ U=unsigned int * ], blah, blah, blah */

    return 0;
}
Русский военный корабль идёт ко дну!
Re: Alias cast
От: Masterkent  
Дата: 21.03.09 13:48
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Никого не посещала такая идея насчёт проверки strict aliasing ?


А для чего нужна _такая_ проверка? Возьмём, к примеру, код:

#include <iostream>

struct X
{
    int n;
};

int main()
{
    X x = { 5 };
    int *pn = reinterpret_cast<int *>(&x);
    X *px = reinterpret_cast<X *>(pn);
    std::cout << *pn << std::endl;   // output: 5
    std::cout << px->n << std::endl; // output: 5
}

Стандарт гарантирует, что pn будет указывать на x.n, а px — на x. Применим ли тут где-либо вышеописанный alias_pointer_cast?
Re[2]: Alias cast
От: Alexander G Украина  
Дата: 21.03.09 14:21
Оценка:
Здравствуйте, Masterkent, Вы писали:

...

M>Стандарт гарантирует, что pn будет указывать на x.n, а px — на x. Применим ли тут где-либо вышеописанный alias_pointer_cast?


Нет. Но остальные варианты из 3.10/15 можно покрыть.

Насчёт этого

- an aggregate or union type that includes one of the aforementioned types among its members (including,
recursively, a member of a subaggregate or contained union),

можно было бы добавить версию с ослабленой проверкой, которая бы в случае pod composite с любой из сторон выполняла бы приведение.
Русский военный корабль идёт ко дну!
Re[3]: Alias cast
От: Masterkent  
Дата: 21.03.09 15:19
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Но остальные варианты из 3.10/15 можно покрыть.


Тут есть ещё минимум одна проблема: к базовому подобъекту в общем случае нельзя обращаться через lvalue/указатель, полученный reinterpret cast-ом над lvalue/указателем_на_объект производного класса.

Так всё же зачем может понадобиться такой шаблон?
Re[4]: Alias cast
От: Alexander G Украина  
Дата: 21.03.09 15:46
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Так всё же зачем может понадобиться такой шаблон?


Чтобы делать безопасные reinterpret_cast'ы с последующим доступом через результат каста.
Русский военный корабль идёт ко дну!
Re[4]: Alias cast
От: Roman Odaisky Украина  
Дата: 21.03.09 15:48
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Тут есть ещё минимум одна проблема: к базовому подобъекту в общем случае нельзя обращаться через lvalue/указатель, полученный reinterpret cast-ом над lvalue/указателем_на_объект производного класса.

M>Так всё же зачем может понадобиться такой шаблон?

Наверное, чтобы компилятор не имел права игнорировать ++*(X*)(T*)&x?
До последнего не верил в пирамиду Лебедева.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.