На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
  
> Типизировать POD-типы?
    Всем привет!

    Начну с общеизвестных утверждений, ну или типа того ...

    - Чем хороши языки со статической типизацией?
    - Статическая типизация хороша в качестве помощи прогеру, т.к. позволяет искать ошибки на этапе кодирования
    - Чем плохи языки со статической типизацией?
    - Статическая типизация заставляет прогера в ряде случаев делать дополнительные нецелевые действия

    - Чем хороши языки со динамической типизацией?
    - Динамическая типизация позволяет прогеру в ряде случаев не делать дополнительные нецелевые действия
    - Чем плохи языки со динамической типизацией?
    - Динамическая типизация плоха, т.к. не помогает прогеру в поиске ошибок на этапе кодирования

    Ну вот кажется С++ со статической типизацией ... строгой ее назвать "рука не поднимается" (С) Черномырдин
    А все из-за POD-типов.

    Пример 1

    ExpandedWrap disabled
      #include <iostream>
       
      using Counter = int;
      using Size = int;
       
      Counter GetCount(Counter value) {
        return (value > 0) ? value*2 : 0;
      }
       
      Size GetSize(Size value) {
        return (value > 0) ? value : 0;  
      }
       
      int main() {
        Counter count = 1;
        Size size = 2;
        std::cout << GetSize(size)*GetCount(count) << std::endl;
        return 0;
      }

    Код компилируется, запускается, даже что-то считает. Но по названиям видно - смешалось несмешиваемое. Ибо наши using/typedef не образуют новые типы, а только лишь создают алиасы.

    Ну и как нам контролировать уместность/законность передачи, использования там-сям переменной POD-типа? ну чтобы уж точно сказать "мне помогает статическая типизация чисто конкретно, а не просто конкретно" :)

    Пример 2

    Ну подумал-подумал и запилил тяп-ляп вот какой говнокод:

    ExpandedWrap disabled
      #include <iostream>
       
      enum class EnumType {Counter,Size};
       
      template <typename T, EnumType E>
      class ClassInteger {
        public:  
          T value;
          ClassInteger(T init) : value(init) {}
          bool operator>(ClassInteger& other) { return value>other.value;}
          bool operator>(int other) { return value>other;}
          bool operator<(ClassInteger& other) { return value<other.value;}
          bool operator<(int other) { return value<other;}
          ClassInteger& operator=(const int other) { value=other; return *this;}
          ClassInteger& operator=(const ClassInteger& other) { value=other.value; return *this;}
          ClassInteger& operator*(const int i) { value *=i; return *this;}
          ClassInteger& operator*(const ClassInteger& i) { value *=i.value; return *this;}
      };
       
      template <typename T, EnumType E>
      std::ostream& operator<<(std::ostream& os, const ClassInteger<T,E>& i) {
        os << i.value;
        return os;
      }
       
      using Counter = ClassInteger<int,EnumType::Counter>;
      using Size    = ClassInteger<int,EnumType::Size>;
       
      Counter& GetCount(Counter& value) {
        return (value>0) ? value*2 : value*0;
      }
       
      Size& GetSize(Size& value) {
        return (value>0) ? value*3 : value*2;
      }
       
      int main() {
        Counter C1 = 1;
        Counter C2 = 2;
        std::cout << GetCount(C1)*GetCount(C2) << std::endl;
        Size S = 3;
        std::cout << GetSize(S) << std::endl;
        // std::cout << GetCount(C1)*GetSize(S) << std::endl; // <--- тут ожидаемая ошибка
        return 0;
      }

    В принципе - получилось что хотел - две переменные с одинаковыми "кишками", но разные по типам.
    И их уже непреднамеренно не смешаешь, не перепутаешь при передаче.

    Но реализация этой хотелки у меня получилась - дичь!

    Собственно вопрос к обсуждению.
    Как можно более изящно и без дополнительного оверхеда типизировать POD-типы в С++?
      Как по мне, это вполне себе изящное решение. Ты определил для компилятора идиому новых типов, описал их свойства и правила взаимодействия между разными объектами этой идиомы. На каком-нибудь Прологе проще не получилось бы.
      P.S. У Александреску, например, был целый паттерн value2type, который он применял как раз для того, чтобы "преобразовывать" значения в типы.
        А как быть с оверхедом? Был тип, который влезал в регистр, а что стало?
          А ничего не стало. Ты же поля в класс не добавлял. Для пущей уверенности ещё final можешь сделать, чтобы компилятор смог соптимизировать, если ты где-то передашь этот класс по константной ссылке.
            Цитата OpenGL @
            Ты же поля в класс не добавлял

            Как не добавлял? А "T value;"?
              Хорошо, не добавлял полей кроме T.
                Цитата Qraizer @
                Как по мне, это вполне себе изящное решение.

                Немного взял паузу - поразмыслил ... и все же нет! Ну если только - как "заготовка". И, кстати, я там малеха слукавил - ниже в рассуждениях увидишь.
                Вот смотри, распишу по пунктам:
                1. Оверхед - и это важно. Ты не ответил. Ну ланна - тут можно и в ассемблере глянуть. Почему-то уверен, что даже с оптимизацией он будет.
                2. Избыточность операций. Я накидал общие. И они дают возможность выполнять операции между одинаковыми типами. Но есть нюанс. Если, по смыслу Count*Count==Count, то Size*Size!=Size, т.к. последняя операция дает площадь. Нужно придумать механизм запрета отдельных операций для отдельных производных типов от одинаковых EnumType.
                3. Недостаточность операций. В качестве контроля передачи нужного типа - моя кухня подходит. А если все же нужно дать возможность Size*Count==Size? Ведь это правильно! Нужно придумать механизм разрешения отдельных операций для отдельных производных типов от разных EnumType.
                По второму вопросу - не знаю, может как-то через traits мутить, передавать его через третий шаблонный параметр?
                По третьему вопросу - вырисовывается нескончаемая портянка определения попарных операций, не?
                  Цитата JoeUser @
                  Оверхед - и это важно.

                  Оверхед зависит от того, сможет ли компилятор заинлайнить функции. Если заинлайнит, то никакого оверхеда у тебя не будет т.к. ему просто неоткуда больше взяться.
                    JoeUser, раз уж ты задумался о единицах измерения, то это уже совсем другая парадигма. К типам она не имеет отношения, она посредством них может быть реализована. Т.е. самоцель уже не типизировать PODы, а как типы применить в качестве инструмента. Ты б сначала эту парадигму хорошо продумал, что ты хочешь ею решить, а потом уже искал методы реализации. На скорую руку: посмотри std::common_type.
                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                    0 пользователей:


                    Рейтинг@Mail.ru
                    [ Script execution time: 0,0301 ]   [ 16 queries used ]   [ Generated: 28.03.24, 19:02 GMT ]