Как стать автором
Обновить
-19
0

Пользователь

Отправить сообщение
Что произошло? У нас было очень простое правило. Но рекурсивное применение этого правила породило структуру, выглядящую очень сложным образом. Здравый смысл подсказывает нам, что так не бывает. Но в действительности такое спонтанное зарождение сложности встречается повсеместо при применении простейших правил к простейшим структурам.
Рядом спорное утверждение о «простом правиле {{x, y}, {x, z}} → {{x, z}, {x, w}, {y, w}, {z, w}}», и о том, что «так не бывает», и сразу же констатация повсеместной встречаемости эффекта в простейших случаях. Воспринимается как манипуляция. К тому же (как обычно у автора) от начала и до конца всё щедро присыпано громкими благоговейными фразами и замечаниями о прорывных результатах и огромном «фундаментальном» значении открытий.

Спасибо, хороший конспект по основам и свойствам отношений. Всё хотел себе подобное записать.
Чтобы почувствовать отношения их разнообразие, мощность мне пришлось вручную создать каталог бинарных отношений над множеством из 3-х элементов, который включил все (боле 500 отношений) отношения. После этого «дошло» или «зашло»об отношениях.
:-))
Мы каждый день сталкиваемся с подобными задачками в различных формах.
Скорее нет.
Всегда удивляли многомудрые теоретики необольшевизма демократического толка. Если кто-то не может отстаивать свои идеалы и жить в мире, не затыкая оппонентам рот и не ограничивая их свободы (по сравнению со своей), то они обречены на поражение, а их мир обречен на вырождение. Какими бы истинно либеральными и правильно демократическими они сами себе не казались.
Ну вообще да: LSP исключительно про subtyping. А ООП со своим наследованием сбоку-припёку.
Да нет, в ООП это тоже так. Просто модель надо строить соответствующим задаче образом.
Не очень понято, что вы хотите сказать: что для одних задач квадрат это прямоугольник, а для других нет — в зависимости от того, как модель построить под решение конкретной частной задачи?
Вот такой еще вопрос для холиваров :-)):
Очевидно, что множество всех квадратов является подмножеством множества всех прямоугольников. Откуда необходимым и достаточным образом следует, что квадрат — это прямоугольник (квадрат «является» прямоугольником). В ООП это не так. Объясните почему.
Ну кстати, если вы изучали историю «соседнего братского государства», то в его официальной национально-государственной историографии (трепетно пестуемой уже не одно столетие, в новое время даже институт национальной памяти создали) это государство и его народ никому «братским» никогда собственно не являлся и не является, а для его «восточного соседа» как бы даже наоборот. Это многим претит, и в этом по-моему состоит корень всех проблем и связанных с ними событий: при прочих равных условиях люди легко, без страха и сожаления посылают всё это восвояси. Но вообще от державных трезуба и булавы патриотизма, которыми бьют по голове со школьной скамьи, невозможно увернуться, чтобы не ушибило.
Я не догадался, что будет выход на эту тему, и что это позволит любому мимопроходящему желающему особо не напрягаясь и не вдаваясь в смыслы, использовать избитый шаблон, и на контрасте выразить своё неприязненное отношение к «российской правящей верхушке». Это так просто случайно вышло, не благодарите.
Он помог той самой риторикой. Когда русские работали с американцами, всем было норм. Когда русские начали говорить, что американцы зависят от русских, американцы сказали: «Ну окей. Так, кто там у нас? Маск? На тебе несколько миллиардов. Кто еще? «Boeing»? И тебе на несколько миллиардов». И все, проблему закрыли. Не сразу, конечно. Несколько лет все-таки сохранялась эта зависимость от РД-180.
Наивно думать что американцы выделили деньги Маску и Боингу из-за риторики, пусть даже Рогозина. Им нужны были свои пилотируемые корабли, они их делали и проектировали начиная со времен последнего запуска шатла. Причем здесь РД-180?
РД-180 — классные двигатели, их бы покупали и покупали, если бы наши про это помалкивали. И летали бы на них американцы спокойно, и платили бы еще миллиарды за десятки этих движков. Но мы начали играть в «отключим вам газ». В результате американцы взяли, сделали движки на метане, и им уже не нужны эти РД-180, несмотря на всю их надежность. Доигрались в пропаганду.
Да неужели на пропаганду обиделись? Посчитайте против скольких российских предприятий ОПК были введены санкции с 2014 года. Некоторые из них вообще ни сном ни духом ни к чему нельзя привязать — они виноваты лишь в том, что они просто есть. Все диктуется психологией политической ксенофобии записанной на подкорке у американского истеблишмента.
Очень круто. Как будто какой-нибудь ФП-шный хаскелевский Data.List в C++ перетащили. Надо добавить в Boost. Правда от 90% до 99% программистов все-равно будут у себя писать циклы по-старинке, максимум — с использованием известных функций STL.
Но если вы можете предложить общего предка для этих сущностей без делания down-cast при каждом использовании, мне интересно будет почитать. Желательно с примерами кода.
Я это просто расцениваю как предложение реализовать паттерн Visitor для некоторых упомянутых вами названий сущностей. Для лучшего восприятия я оставил канонические имена для функций accept и visit. И что?
int main()
int main()
{
  // example: Cartesian product of two independently arranged vectors 

  std::vector< Unit* > unit_sequence = {
    new ModuleUnit(),
    new FunctionUnit(),
    new LoopUnit(),
    new LoopUnit(),
    new FunctionUnit(),
    new ModuleUnit()
  };
  
  std::vector< Pass* > pass_stages = {
    new SanityCheckPass(),
    new ConstPropagationPass(),
    new FunctionInliningPass(),
    new LoopFusionPass(),
    new LoopUnrollingPass(),
    new SanityCheckPass()
  };
  
  for (Unit *u : unit_sequence)
  {
    printf("\n%s: %p\n", typeid(*u).name(), (void*)u);
    for (Pass *p : pass_stages)
    {
      printf("* %s\n", typeid(*p).name());
      p->visit(*u);
    };
  };


  // example: more precisely arranged pipeline
  using stage_t = std::pair< Unit*, Pass* >;
  
  std::vector< stage_t > stages = {
    stage_t{ new ModuleUnit(), new SanityCheckPass() },
    stage_t{ new ModuleUnit(), new ConstPropagationPass() },
    stage_t{ new FunctionUnit(), new FunctionInliningPass() },
    stage_t{ new FunctionUnit(), new SanityCheckPass() },
    stage_t{ new LoopUnit(), new LoopFusionPass() },
    stage_t{ new LoopUnit(), new LoopUnrollingPass() },
    stage_t{ new ModuleUnit(), new SanityCheckPass() },
  };
  printf("\nStage pipeline:\n");
  for (auto [u, p] : stages)
    p->visit(*u);

  return 0;
}

Вы считаете это ответом на вопрос?
Ваше утверждение: «В данном случае я никаких костылей не наблюдаю. Как по мне, это совершенно нормальное решение, которое хорошо ложится в данный концепт» против моего: «Если для вас это нормальное решение, то тогда именно поэтому вы костылей не наблюдаете». Достаточно корректно. Но если уж действительно отвечать на вопрос
Можете рассказать, с каких это пор классы-адаптеры стали костылями?
То это начинает происходить с того момента, когда вместо того, чтобы ясно и идеоматично в терминах прикладной области описывать происходящие в ней процессы, код начинает фонтанировать сущностями, необходимыми только для поддержания работоспособности применяемых идиом и дизайн-концепций.

Сторонний наблюдатель, которому впервые необходимо разобраться в коде, ещё сможет быстро понять что это за сущности PassConcept/PassModel введены, т.к. они обозначены близкими к предметной области названиями.

Почему вдруг PassManager превращается в PassModel на основании того, что в нем тоже присутствует метод, побуквенно совпадающий с имени метода из PassModel, но семантически никак ему не эквивалентный (и он начинает сам выступать как PassModel, сам себя вкладывает, и сам себя вызывает) можно объяснить лишь дизайнерским выпендрёжом — чтобы было красиво. Из-за постоянно возникающих безответных вопросов «зачем?, почему это так надо?» разобраться с этим финтом (без подсказки в виде отдельной статьи или презентации о «новой интересной идиоме») уже сложновато.

Ну и под конец когда на сцене вдруг появляется адаптер из PassManager<Function> в PassManager<Module>, то это получается примерно как, если бы сначала по-пуристски заявлять что «яблоки и огурцы не должны иметь общего предка, это прямое нарушение LSP», а затем сразу же: «у нас здесь яблоки, но мы сделаем для них адаптер к огурцам, потому что у нас тут коллекция вообще-то по огурцам».
А тут я видимо где-то отстал: можете рассказать, с каких это пор классы-адаптеры стали костылями? В данном случае я никаких костылей не наблюдаю. Ниже привожу код данного адаптера. Как по мне, это совершенно нормальное решение, которое хорошо ложится в данный концепт.
Если для вас это нормальное решение, то тогда именно поэтому вы костылей не наблюдаете. По иронии в GoF паттерн Visitor описывается как раз на примере с компилятором.

Повторюсь еще раз: классы Module, Function и Loop не образуют иерархию и не должны этого делать. Введение для них общего предка — прямое нарушение LSP.
А как определить должны или не должны? А как определили что все PassModel вместо этого должны иметь общего предка PassConcept? Не могу понять это «нарушение LSP» или нет.

Потому что введение дополнительных сущностей только ради того, чтобы использовать паттерн — это не очень хорошая практика проектирования.
Это вы про «про одну интересную идиому Concept-Based Polymorphism» или про Visitor?
Это бы хорошо работало, если бы все пассы работали с единой иерархией классов: нечто, от чего наследуется модуль, функций и т.д. В этом случае мы могли бы сделать визитор на каждую из этих сущностей и, таким образом, применить паттерн Visitor.
Да, конечно. Тут сложно судить со стороны т.к. не понятно почему, находясь под полным контролем разработчика, классы IR-сущностей не сделаны под общим предком с некоторым интерфейсом, который бы давал последующую возможность добавлять выполнение относительно произвольных действий над ними с помощью Visitor.

Возможно смысл был в том, чтобы сделать новый PassManager никак не трогая определения классов IR-сущностей, а также и определения классов PassT-сущностей.

Но здесь тоже «без дополнительных crutches» не обходится: PassT-типы завернули в иерархию PassConcept — PassModel, но поскольку в этом случае виртуальная функция-член run не может быть шаблоном, зависимость от IR-типов вышла на уровень классов и далее перешла и на класс PassManager (т.е. это всё каждый раз совершенно разные типы в зависимости от IR). И чтобы зарегистрировать все проходы в красивый единый вектор требуется некий костыль в виде некоего адаптера
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
Давайте посмотрим на код, в чем здесь проблема? Мы видим, что в этой иерархии методы запуска прохода различны в зависимости от того, над чем они должны выполняться (над функцией — runOnFunction, модулем — runOnModule, циклом — runOnLoop и тд). В свою очередь, это делает невозможным обрабатывать коллекцию проходов, которые работают с разными IR сущностями, единым способом (собственно применять полиморфизм). Казалось бы, очевидно, как сделать правильно: нужен виртуальный метод run, который будет переопределяться в наследниках. Но тут же возникает проблема: у методов run в классах-наследниках будет разная сигнатура, поскольку передается параметр всегда своего типа — функция, модуль и так далее. Значит, придется делать фиктивный базовый класс для Module, Function и т.д., передавать в run указатель на этот класс, а внутри метода делать down-cast, в зависимости от того, что за объект находится по данному указателю. И начинается что-то странное: при появлении новой нижестоящей сущности мы вынуждены теперь переписывать каждый раз вышестоящий код, что противоречит всем принципам проектирования.
Паттерн проектирования Visitor (двойная диспетчеризация) формализован как раз для таких случаев, нет?
Заапрувили мой форкаст, нужно сейлзам кипиай инкризить.
Спринт кончается, а у нас инкремент не выкачен.
Асайнь таску асап!
Это какой-то ново-олбанский.
С __attribbute__(( cleanup(...) )) могут быть нюансы, связанные с повторным заходом в область видимости.
Пример:
int main(void)
{
  {
    struct Obj *test __attribute__(( cleanup(Obj_cleanup) )) = calloc(1, sizeof(struct Obj));
    fprintf(stdout, "Obj allocated at %p\n", (void*)test);
    
    fprintf(stdout, "Quitting the test scope...\n");
    goto main__sub1;
  
main__exit:
    return 0;
  } // <-- Obj_cleanup is executed here twice, even though the control flow passes the 'test' definition only once

main__sub1:
  fprintf(stdout, "The test scope has been quit.\n");
  goto main__exit;
}

Вывод:
Obj allocated at 00000000005F6880
Quitting the test scope…
Obj_cleanup() for 00000000005F6880
The test scope has been quit.
Obj_cleanup() for 00000000005F6880

А вот clang отслеживает наличие на границе области видимости выполняющейся cleanup-функции, зависящей от переменной test, и отказывается компилировать такой код.
Здорово. Почему не на GitHub/GitLab? Требуется доводить проект до состояния более понятного для конечного пользователя или заинтересованного участника.
По мне так этим двум языкам давно уже надо развязаться.
Возможно так. Но наверно будет не совсем хорошо, если при развитии в C завезут фичи, которые уже есть в C++, а они будут синтаксически или лексически реализованы по-другому. Вот эта общая спецификация, я так понял, есть попытка как-то формализовать одинаковости/различия в свете данного аспекта. Если оба комитета договорятся и какие-то базовые вещи в обоих языках будут вести согласовано, будет хорошо.
В дополнение к общей картине по современному C нужно сказать, что ведется и такая работа — A common C/C++ core specification, а так же упомянуть замечательную книгу от автора этого блога — Jens Gustedt, соредактора текста последнего стандарта C18 (ISO/IEC 9899:2018), — Modern C, которая свободно доступна.
Ну и собственно сам последний драфт C2x (working draft — February 5, 2020) — n2479, в котором можно посмотреть текст актуального C18(C17) 9899:2018.
1
23 ...

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность