На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела:
1. Название темы - краткое описание кто/что против кого/чего
2. В первом сообщении - список параметров, по которым идет сравнение.
3. Старайтесь аргументировать свои высказывания. Фразы типа "Венда/Слюникс - ацтой" считаются флудом.
4. Давайте жить дружно и не доводить обсуждение до маразма и личных оскорблений.
Модераторы: Модераторы, Комодераторы
Страницы: (495) « Первая ... 354 355 [356] 357 358 ...  494 495  ( Перейти к последнему сообщению )  
> Delphi vs C++ vs C# , ну и Java немножко, где-то ближе к старшему байту номеров страниц
    Цитата korvin @
    Quraizer, эм... вроде ж ты же предлагал тут пример определения наследования в compile-time с поиощью шаблонов, может его прикрутить вместо ручного указания иерархии?

    Ну, там же в комментарии написано
    ExpandedWrap disabled
      /* Самый базовый класс должен быть последним.
        Впрочем, заглянув к Александреску на книжку чаю, поиск самого базового из предоставленных
        можно автоматизировать. Я не стал копить сюда лишнего метакода, его и так выше крыши. */

    Или о чём речь?
    Скрытый текст
    Господа, korvin посмотрел портянку шаблонов и не оставил ни одного едкого комментария. Ладно ли с ним? Или всё же мы его теряем?

    Цитата Qraizer @
    Угу. Мне тоже. С другой стороны это позволяет некоторые классы в иерархии не использовать как целевых кандидатов

    Ну, во всех попытках библиотечных реализаций, что мне попадались, это решалось динамической регистрацией. Другое дело, что знание иерархии compile-time даёт большие преимущества. Но при таком знании я не очень понимаю, зачем ещё какие-то методы в диспетчеризуемых классах - надо код внимательно смотреть.
      korvin, я имел в виду получить bool-евый признак, является ли некий D производным от некоего B. Т.е. нужно задать конкретные D и B. Иерархию так можно отсортировать, но не построить.
      Сортировать, кстати, не требуется, классы одной иерархии могут находиться в списке в произвольном порядке. Главное - получить взаимнооднозначное соответствие между AcceptorBase и узлами селекторов LastAcceptorCaller, InterAcceptorCaller и FirstAcceptorCaller, а это обеспечивается не взирая на порядок перечисления. Когда вызывается apply() из конкретного диспетчера, тогда становится важно, кто от кого наследован. Но это уже не наша проблема, т.к. мы закончили, а компилятора, а у него эта инвфомация и так есть.
      Единственное, что мне тут нужно, это получить "самый базовый" класс в списке, т.к. именно он является декларирующим интерфейс для производных и именно он задаёт базовый тип парамтра для мультиметода. Пока он просто должен быть указан последним в списке, но никто не мешает заюзать вон то распознавание, чтобы это автоматизировать. Просто это самое простое из всего, что ещё остаётся сделать, поэтому совсем несрочное.
        и еще интересно как разруливается ситуация ромба при множественном наследовании.

        т.е. например:
        ExpandedWrap disabled
          class A1;
          class A2;
          class B : A1, A2;
           
          void apply (x);
          void apply (A1 x) {
              //
          }
           
          void apply (A2 x) {
              //
          }
           
          B b;
          apply(b); // ?


        Добавлено
        Цитата MyNameIsIgor @
        Скрытый текст
        Господа, korvin посмотрел портянку шаблонов и не оставил ни одного едкого комментария. Ладно ли с ним? Или всё же мы его теряем?

        Скрытый текст
        я долго отходил от шока, а когда отошел, у меня появились вопросы =)
          Цитата korvin @
          ведь по твоей иерархии D1 -- наследник Base, но сам класс D1 наследником Base не является
          Вообще-то является. Там в среднем фрагменте только объявления, чтобы убедиться, что декомпозиция получилась качественной. Определния в нижнем, под спойлером. Если же что-то напутается в списках, просто не скомпилится. Диспетчер на самом деле "всего лишь" востанавливает динамические типы, т.е. делает из вызова
          ExpandedWrap disabled
            disp(base*, other*, base*)
          вызов вроде
          ExpandedWrap disabled
            apply(D2*, O3*, D1*)
          , где D2*, O3* и D1* - это настоящие типы объектов, скрывающиеся под базовыми интерфейсами. А дальше работает простая перегрузка, где компилятор будет пытаться неявно кастануть параметры так, чтобы apply(D2*, O3*, D1*) удовлетворила одной из предоставленных. В данном примере это будет apply(D1*, Other*, Base*). Если при этом получится неуспех или неоднозначность, будет ошибка компиляции.
            Цитата Qraizer @
            Вообще-то является. Там в среднем фрагменте только объявления, чтобы убедиться, что декомпозиция получилась качественной.

            а, пардон, все забываю об этой особенности
              Множественное наследование поддерживается, если множественные базы уникальны, неважно, сомещаются они или разделяются. Используется ли для базы виртуальное наследование или простое, на стадии разрешения перегрузки компилятор всё разрулит сам.
              А вот множественные неуникальные базы, как в твоём примере, не поддерживаются. Просто по причине того, что "самый базовый класс" становится неуникальным, поэтому doIt() в абстрактном диспетчере не имеет однозначной сигнатуры. Это одна из большим проблем, которую я намерен обдумать. Впрочем, не думаю, что возьмусь за неё в ближайшем будущем, ибо в C++03 это совсем уж напряжно. Может быть на вариадиках имеет смысл. Технически тому нет препятствий, нужно просто перегрузить doIt(), что достигается опять же генерацией иерархии по списку. Просто мне не удалось даже придумать приемлимого простого способа для пользователя задавать такие сигнатуры библиотеке.
              Сообщение отредактировано: Qraizer -
                а способ, используемый в CL не нравится? там это разруливается указанием способа комбинации в самом обобщенном методе, например:
                ExpandedWrap disabled
                  (defgeneric m (x)
                    (:method-combination progn))
                   
                  (defclass a1 () ())
                  (defclass a2 () ())
                  (defclass b  (a1 a2) ())
                   
                  (defmethod m progn ((x a1))
                    (format t "a1~%"))
                   
                  (defmethod m progn ((x a2))
                    (format t "a2~%"))
                   
                  (method (make-instance 'b))
                  ; выведет на печать:
                  ; a1
                  ; a2

                т.е. при комбинации progn просто последовательно выполняются все методы предков
                соответственно есть, например, комбинация +, которая суммирует результаты методов (они должны возвращать число в таком случае). ну и можно писать свои комбинации.
                Сообщение отредактировано: korvin -
                  Цитата MyNameIsIgor @

                  Ну, во всех попытках библиотечных реализаций, что мне попадались, это решалось динамической регистрацией. Другое дело, что знание иерархии compile-time даёт большие преимущества. Но при таком знании я не очень понимаю, зачем ещё какие-то методы в диспетчеризуемых классах - надо код внимательно смотреть.
                  Основная идея осталась той же. Интрузивный Accept() для очередного параметра восходяще кастует конкретный акцептор к абстрактному для этого класса, одному из лучей веера, и вызвает его Accept(), а тот в свою очередь, будучи перекрыт в селекторе, обрабатывается в одном из его узлов, где мы имеем L - восстановленный тип. Итого два виртуальных вызова на каждый парамтер. Остальые вещи оптимизируются на ура. И ещё есть где соптимизировать, но не до того пока. Вот уменьшить количество виртуальных вызовов было бы неплохо, но с такой идеей реализации не получится.
                  Можно вспомнить про вариант избавиться от интрузивности. Даже в C++03 есть некий аналог метода typeinfo::hash_code(). Это адрес экземпляра typeinfo. Согласно Стандарту он всегда одинаковый для одного и того же типа, а время жизни объектов typeinfo до конца программы. Вопрос в том, покроет ли поиск адресов по хеш-таблице всего-то два виртуальных вызова?.. Сомневаюсь.

                  Добавлено
                  korvin, вот этим скорее к D_KEY-ю. Тут я мало что смогу сказать. Разве что "никто не мешает пользователю самому написать такую или любую другую комбинацию", если я правильно понял, что там в Лиспе происходит, но как я уже говорил, у меня пока нет идей, как предоставить пользователю удобный инструмент для этого.
                  Сообщение отредактировано: Qraizer -
                    Цитата Qraizer @
                    "никто не мешает пользователю самому написать такую комбинацию", если я правильно понял, что там в Лиспе происходит

                    не, я имел в виду, что можно писать свои правила комбинации, а не на каждый конкретный случай отсутствия реализации метода.

                    впрочем комбинации не являются обязательной фишкой.
                    Сообщение отредактировано: korvin -
                      Цитата Qraizer @
                      Основная идея осталась той же. Интрузивный Accept() для очередного параметра восходяще кастует конкретный акцептор к абстрактному для этого класса, одному из лучей веера, и вызвает его Accept(), а тот в свою очередь, будучи перекрыт в селекторе, обрабатывается в одном из его узлов, где мы имеем L - восстановленный тип.

                      Хз-хз... Александреску в статическом диспетчере, если память не изменяет мне с другим, не понадобилось ничего иметь в диспетчеризуемых классах. Надо просто смотреть и разбираться, а я сейчас спать хочу...
                      Цитата Qraizer @
                      но как я уже говорил, у меня пока нет идей, как предоставить пользователю удобный инструмент для этого.

                      Не знаю на сколько реализуемо, но, например, пользователь мог бы установить свой функтор, которому запихивались бы все кандидаты в случае коллизии, а там уж пусть хоть в шахматном порядке вызывает. А по умолчанию кидать исключение. Это как идея...
                        Цитата MyNameIsIgor @
                        например, пользователь мог бы установить свой функтор, которому запихивались бы все кандидаты в случае коллизии, а там уж пусть хоть в шахматном порядке вызывает. А по умолчанию кидать исключение. Это как идея...

                        ну собственно примерно так и происходит, берутся методы непосредственных родителей и... комбинируются определенным способом. =)

                        а в случае отсутствия метода у какого-то из родителей, он просто игнорируется.

                        Добавлено
                        кстати, этот механизм не имеет непосредственного отношения к мультиметодам, так что, на мой взляд, его бы неплохо реализовать вне этого контекста, тогда б его можно было использовать и для обычных методов при множественном наследовании классов.
                        Сообщение отредактировано: korvin -
                          MyNameIsIgor, Александреску использует иной принцип. Там, если память мне не изменяет, вместо, как в этой реализации, полного комплекта перегруженных методов со статическим связыванием, которыми заполняется некая суперматрица (матрица размерности, равной количеству параметров мультиметода; например параллелепипед в случае тройной диспетчеризации) по мере генерации селекторов и их линейных иерархий, генерируются нешаблонные дружественные функции, определённые сразу при их объявлении внутри диспетчера. Она, кстати, более экономична, вроде бы, т.к. генерируются только реально восстребованные функции, а не всевозможные, как тут, но зато его мультиметоды хуже поддаются декомпозиции между библиотечным кодом и кодом пользователя, сложнее масштабируются, базируются на dynamic_cast<> вместо полиморфных вызовов и неинтрузивные как раз благодаря контейнеру указателей на экземпляры typeinfo и требуют наполнения этого контейнера в run-time.
                          Тут я ограничился поддержкой до 10 параметров, и не составляет большого труда увеличить их количество, не влияя на пользовательский код. К тому же это ограничение С++03, в C++11, благодаря вариадикам, количество параметров не будет ограничено никаким максимумом изначально. У Александреску же изначально реализованы только 2 параметра, и увеличить их количество можно, только если с оглядкой на пользователя. Т.е. вот понадобилось мне пять параметров, я как-то модифицировал диспетчер Александреску. Причём я в своё время даже спустя 15 минут не понял, что и где надо менять, чтобы добавить 3-й параметр. Не важно, сложно это было или просто, важно, что имея пятипараметровый диспетчер, я не имею при этом четырёхпараметрового. Точнее имею, но мне придётся подгонять свой пользовательский обощённый (если он обощённый) код под четыре параметра тоже, т.е. я не могу иметь единый универсальный многопараметровый пользовательский код.
                          Этот же принцип реализации мне сразу приглянулся, ещё когда Flex Ferrum выложил упрощённый вариант двойной диспетчеризации на вариадиках. Мысленно заменив их на списки типов, ибо полноценного C++11 ещё нет ни у кого - да и когда появится, всё равно найдётся куча народу, сидящая на VC8 и 10 - я уже тогда увидел преимущества в декомпозиции и масштабируемости. Правда, если на вариадики переложить вариант Александреску, может быть он тоже окажется приемлимым, окромя крайне низкой эффективности dynamic_cast<> и логарифмического поиска по контейнеру по сравнению с парой полиморфных вызовов. Я его слабо помню.

                          Добавлено
                          Вот счас краем глаза глянул... похоже, я в натуре не имею четырёхпараметрового диспетчера, если имею пятипараметровый. Даже в библиотеке. Что-то у меня такое ощущение, что для каждого количества параметров нужен будет свой уникальный диспетчер.
                          Сообщение отредактировано: Qraizer -
                            MyNameIsIgor, не считаешь ли ты это похожим на "поделку индусского джуниора" ? :D
                            ExpandedWrap disabled
                               /* Создание кортежа разным количеством параметров. Без копипаста в C++03 никак, увы. */
                               Tuple(L t0):                      Field<TList<L, R>, L>(t0) {}
                               template <typename T1>
                               Tuple(L t0, T1 t1):               Field<TList<L, R>, L>(t0), base_type(t1) {}
                               template <typename T1, typename T2>
                               Tuple(L t0, T1 t1, T2 t2):        Field<TList<L, R>, L>(t0), base_type(t1, t2) {}
                               template <typename T1, typename T2, typename T3>
                               Tuple(L t0, T1 t1, T2 t2, T3 t3): Field<TList<L, R>, L>(t0), base_type(t1, t2, t3) {}
                               
                              ...много-много копипасты
                              IL_Agent, в текущем стандарте иначе никак :(
                                IL_Agent, соизволите ли вы читать тему, в которую постите?
                                Цитата MyNameIsIgor @
                                Цитата Qraizer @
                                MyNameIsIgor, у меня произвольнопараметрические мультиметоды на C++03 именно такими и получаются . На variadic templates, конечно, изящнее.
                                Да, но у нас есть списки типов и boost.preprocessor.


                                Добавлено
                                Цитата D_KEY @
                                IL_Agent, в текущем стандарте иначе никак

                                Как раз в текущем всё прекрасно и без препроцессора :D
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (495) « Первая ... 354 355 [356] 357 358 ...  494 495


                                Рейтинг@Mail.ru
                                [ Script execution time: 0.5002 ]   [ 15 queries used ]   [ Generated: 31.07.26, 03:46 GMT ]