1. C++ / Говнокод #13741

    −1

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    #include <iostream>
    using namespace std;
    
    struct NG
    {
    	template<class T>
    	struct get
    	{
    		typedef typename T::f type;
    	};
    };
    
    template <typename T, typename NameGetter>
    struct has_member_impl
    {
        typedef char matched_return_type;
        typedef long unmatched_return_type;
        
        template <typename C>
        static matched_return_type f(typename NameGetter::template get<C>*);
        
        template <typename C>
        static unmatched_return_type f(...);
        
    public:
        static const bool value = (sizeof(f<T>(0)) == sizeof(matched_return_type));
    };
    
    template <typename T, typename NameGetter>
    struct has_member{
       enum { value = has_member_impl<T, NameGetter>::value };
     };
     
     class T{};
    
    int main() {
    	cout<<has_member<T, NG>::value;
    	return 0;
    }

    http://ideone.com/4LDGhZ

    Запостил: LispGovno, 04 Сентября 2013

    Комментарии (36) RSS

    • Что по вашему нужно написать в NG, чтобы класс-функция has_member стала определять наличие метода или хотя бы члена с именем указанным каким-то образом в NameGetter?

      has_member - чужой велосипед, а примера записи NameGetter в статье не указано.
      Ответить
      • Нашел другую статью, ответ здесь:
        http://www.gockelhut.com/c++/articles/has_member
        Не смотрите ниже, если хотите поломать ещё мозги над задачей:



        struct check_read_write
        {
            template <typename T,
                      int (T::*)() const = &T::read,
                      void (T::*)(int) = &T::write
                     >
            struct get
            { };
        };
        Ответить
    • У меня только один вопрос: Для чего это нужно? Ведь, если нужного метода в классе нет, то код не скомпилируется.
      Вот если бы это как-то прикрутить для виртуальных функций, чтобы проверять их конкретизацию в дочерних классах... Я знаю про override (везёт же некоторым), но от этого не легче.
      Ответить
      • Ты хоть по ссылке то сходи:
        template <typename T>
        typename std::enable_if<has_print<T>::value, std::ostream&>::type
        operator<< (std::ostream& stream, const T& value)
        {
            value.print(stream);
            return stream;
        }
        Скомпилируется, даже если метода нет.
        Ответить
        • Если метод не используется, то есть ли разница, есть ли этот метод или его нет?
          А вот ниже озвученный вариант с читабельным ассертом мне нраица. В таком контексте код даже и не говно, а весьма полезная няшка.

          Пойду читать ссылку.
          Ответить
          • has_member или check_read_write можно переделать так, чтобы работало если не знаешь сигнатуры функции, а знаешь только её имя. Для этого достаточно вспомнить, что указатель на член каститься в void*.

            Ну и в любом случае не работает с шаблонными методами. А с перегружеными работает не так приятно.
            Ответить
            • > указатель на член каститься в void*

              Нет, не кастится. void* минимум вдвое меньше указателя на член.
              Ответить
              • реинтерпреткастом? Кастится же.
                (void*)&T::foo
                Ответить
                • int a = 0xffff;
                  char b = (char)a;
                  a = (int)b;


                  Это я в более простых типах продемонстрировал проблему. Указатель на метод гораздо сложнее указателя на функцию.
                  Ответить
                  • Ну он же не предлагает потом скастованную хрень юзать...

                    Идея в том, что указатель на существующий член reinterpret_cast'ом с горем пополам кастуется в void *, а указатель на несуществующий член - нет. Для проверки существования этого достаточно. Юзать результат каста никто не будет...
                    Ответить
                    • Чтобы что-то скастовать, это что-то надо получить. Чуть ниже эту мысль полнее озвучил. Если в конец не запутался.
                      Ответить
            • Даже не так, не в размере указателя дело. Дело в том, что важно не только имя функции, но и сигнатура. Ведь в шаблонах идёт проверка типа, а тип функции это её аргументы и тип результата. Чтобы преобразовать тип метода в void* сперва нужно этот тип получить, а проверка существования этого типа и является исходной задачей.

              Можно попробовать сделать ещё шаблонной шаблонной, чтобы с шаблонными методами заработало.)))
              Ответить
              • > можно переделать так, чтобы работало если не знаешь сигнатуры функции, а знаешь только её имя
                Вот только для этого и нужен каст в void ;) Чтобы проверять имя, не обращая внимания на сигнатуру... Ну если я сегодня не туплю...

                > Чтобы что-то скастовать, это что-то надо получить
                Ну вроде как в примере LispGovno оно не окончательно ломается на &test::foo, а просто выбирает другую версию функции f... Скорее всего и с (void*)&test::foo прокатит.
                Ответить
            • Уйди, обрубок.
              Ответить
        • Сходив по ссылке буду утверждать, что ты обоснованно поместил этот код в говно.)
          Килограммы шаблонных выкрутасов там, где достаточно обычной перегрузки для базового класса иерархии. Возможно, нескольких перегрузок для разных деревьев. Но, при правильной иерархии, скорее всего у них один предок с печатающим интерфейсом.
          И говно разруливается кодом вида:

          struct Base
          {
                ~virtual Base();
                virtual void print( std::ostream& )=0;
          };
          
          operator<< (std::ostream& stream, const Base& value)
          {
              value.print(stream);
              return stream;
          }


          Но шаблонные извращения мне нравятся. Возьму на заметку.)
          Ответить
          • > virtual void print( std::ostream& )=0;
            > virtual
            Ты в скорости исполнения потерял. Они обменивают понятность и скорость компиляции на скорость исполнения.
            Ответить
            • Далеко не факт. На одиночных вызовах это не имеет значения, если же печать происходит в цикле, то всё-равно через интерфейс какого-либо базового класса вызовы идут.
              И одиночные вызовы из конкретного объекта (когда тип известен при компиляции) оптимизирующий компилятор вполне может заинлайнить при его желании.
              Ответить
        • Кому-то лавры Obj-C и его отправкой любого сообщения любому объекту не давали покоя?
          Ответить
      • Ну, видимо, тут примешано немного магии, чтобы проверять в компайл тайме, есть ли у класса метод с нужной сигнатурой или нет. При этом ошибки компиляции не будет, метафункция просто будет возвращать false при отсутствии метода.
        В теории это можно применять для оптимизации обобщённых алгоритмов с graceful degradation (если у класса есть "быстрый" метод - используем его, если нет - юзаем обходной медленный путь) или вывода более внятных сообщений в статик-ассёртах.
        Ответить
    • А в чём говно? 80% буста - подобные грязные хаки с шаблонами. Шаблоны проектировались не для метапрограммирования, это факт.
      Ответить
      • Говна тут нет. Просто пердак немного припекло. Прибежал с вопросом. Уже можно мину совать.
        Ответить
      • > грязные хаки с шаблонами
        > грязные
        Сам то на лиспе, хаскеле и скале пишешь. Ктобы говорил про грязные хаки и извращения. От извращенца слышу.
        Ответить
        • В хаскеле чистые хаки. Они смотрятся как говно, но они функцианально чисты.
          Ответить
          • > хаки
            > функцианально чисты
            Мечтай больше: unsafePerformIO
            Ответить
            • > unsafePerformIO
              Ты просто не понял дзен. unsafePerformIO - это чистилище чистых сишных душ функций.
              Ответить
              • Да я могу и грязные вызывать, чо.
                Ответить
                • Можешь, но это плохо кончится ;) Для грязных функций важна последовательность действий, и количество вызовов. Тут же оно вызовется хер-пойми-когда, и, неизвестно-сколько-раз.
                  Ответить
    • Я всё равно не могу понять этот код...
      Ответить
      • И не нужно. На работу потом не возьмут такого грязного извращенца.

        Я всегда своим говорю, что так делать нельзя и вспоминаю историю бывшего сотрудника:
        http://govnokod.ru/8025
        Он сейчас свою сборку mingw пишет и в маиллисты буста похаживает. А вот работает ли где - не уверен.
        Ответить
        • Петя слегка никсмана напоминает.
          Ответить
          • Интересно, что с ним стало? Никто не слышал?
            Ответить
            • Вот полторы недели назад отвечал в своей теме
              http://www.cyberforum.ru/post5379548.html
              Ответить

    Добавить комментарий