Search
Write a publication
Pull to refresh
56
0
Сергей Садовников @FlexFerrum

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

Send message
Зато реализации можно использовать независимо, а не целым бандлом. Вот, скажем, нужны мне только expected — взял один .h-ник и ни о чём больше не заморачиваюсь. В общем, есть в этом подходе что-то разумное. Особенно если с каким-нибудь folly сравнить, которая для того же expected чёрта лысого тащит.
Вот, кстати, человек занят похожим занятием:
github.com/martinmoene?page=1&tab=repositories

Тут, правда, реализация (бакпорт) фишек из новых пропозалов…
Первый вариант — gcc-specific. Второй — не уверен, что вообще будет работать. Только в C++20 появится __VA_OPT__(), который раскрывает аргумент только в случае, если эллипсис — не пустой. Такие дела.
Угу. Но, как всегда, что-то где-то пошло не совсем так… :D
image
Угу. Всё будет хорошо ровно до тех пор, пока в __VA_ARGS__ будет хотя бы один аргумент. Если же он окажется пуст, то начнутся приключения на ровном месте.
Угу. Поддерживают, только каждый в своём стиле. Даже после официального появления в C++11.
Пара существенных замечаний.
1. Макросы без семантики вызова функций (особенно маленькими буквами) надо выжигать калёным железом. У вас бомба такая же, как min()/max() в заголовках Windows SDK. Достаточно написать где-нибудь:
bool forever = IsForever();

или зацепить заголовок с объявлением:
void Foo(bool forever = false);

И начнётся мат. Сначала компилятора в stderr, потом разработчика в ваш адрес. :)

2. Конструкции типа while(true)/while(false) не проходят через высокий уровень варнингов — компилятор начинает ругаться в стиле «Condition is always true(false)» Если включена опция «варнинги как ошибки», то такая ругань начнёт сильно раздражать. Поэтому лучше такое не использовать, либо сразу включать подавление этих варнингов (что не есть хорошо).
Вот тут, от самого вендора.
Ну, насколько я понял, «visitor» — единственный (можно, наверное, ручками, но там наверняка свихнуться можно) способ обхода AST дерева. И как понять, что ты находишься в условном узле или листе дерева? Как отследить отношение «родитель — ребёнок» в этом дереве?

Да. Можно ещё ручками. Это так. То есть способа 3: DOM-like («ручками»), SAX-like (с помощью визиторов) и XPath-like (с помощью матчеров).
Отношение «родитель-потомок» отследить несложно. Можно идти вверх по дереву (если это возможно, например, в декларациях), либо в визиторе хранить стейт. Вот, например, визитор, который превращает «абстрактный» QualType в нечто вразумительное и пригодное для простого анализа:
TypeUnwrapper
На выходе этот код даёт структуру, хранящую упрощённую информацию о типе.
Понятно, что ничего нерешаемого нет. Просто складывается ощущение, что CLang специально делали так, чтобы усложнить жизнь пользователю.

Думаю, цели были немножко другими. :) Ну а уж как получилось — так получилось.
Честно говоря, из вопроса на SO не очень понятно — какая именно задача решается и зачем нужно знать, на какой глубине вы сейчас находитесь. AST в кланге анализировать не то, чтобы просто — это правда. Сам в презентации об этом красочно рассказывал. Но, с другой стороны, это и не rocket science. И разделение классов узлов AST на группы (statements/expressions, types, declarations) — в общем, обосновано.
LibTooling используется для запуска некоторого анализа на исходном коде (с множеством файлов, при желании) без запуска обычного процесса компиляции.

Строго говоря, это не так. LibTooling используется для упрощения настройки clang frontend — предоставляет механизмы удобного разбора командной строки, созданием окружения для компилятора и т. п. А запустить потом можно любой frontend action. Хоть на препроцессинг, хоть на статический анализ, хоть на полноценную компиляцию. Ну и да: интеграция с ast matchers — отдельная вишенка на торте. Но что-то мне подсказывает, что в переводе об этом ничего не будет.

Скачать и установить (например, с помощью apt-get) все необходимые пакеты.
(Типичный дистрибутив Linux идёт со всем необходимым, кроме subversion).
Смените директорию на ту, в которую вы хотите установить LLVM (например, ~/static_analysis/). Будем называть её директорией верхнего уровня. Выполните следующие команды в терминале:

Сейчас уже проще скачать нужные dev-пакеты из репозиториев и не заниматься убийством собственного жёсткого диска.

Ну и довольно неплохая преза по Clang AST лежит здесь: llvm.org/devmtg/2013-04/klimek-slides.pdf

Лучше 5.0. Когда шестая (dev-сборка) версия будет доступна на конвейерах типа travis или appveyor — не ясно.

Возможно и policy. Но я бы предпочёл какое-то однозначное поведение.

А этот SafePtr — да, берите. Я себе ещё сделаю. :)
А вот это — смотря с какой стороны смотреть. Если формулировать контракт так, что SafePtr даёт безопасный доступ к объекту до тех пор, пока объект жив. Для scoped-указателей этот доступ заканчивается вместе со скоупом. Для shared — пока жив shared. С точки зрения клиентского кода совершенно не важно, как был получен pointer. Важно, чтобы он был «жив».
А вот с этого места по-подробнее. То, что после move'а объект должен переходить в состояние «инициализирован конструктором по-умолчанию» — это, в целом, логично и вписывается в идеологию языка. Ведь для объекта когда-нибудь будет позван деструктор. И точку вызова деструктора компилятор предсказать не может. Поэтому полностью разрушать объект в точке трансфера состояния — нельзя. А вот в каких сценариях появляются, как вы пишете, «неконтролируемые ссылки на элементы вектора» — мне очень интересно. Хотя бы один пример приведите, пожалуйста.
Это тонкий семантический вопрос. :) В случае стековых объектов/объектов в динамической памяти всё, вроде, ясно — вы объявляете переменную (или создаёте объект по new), и у вас появляется некая сущность, над которой можно выполнять операции. Чаще всего — полностью контролируемые вами. Если вы хотите что-то сделать с таким объектом — вы (чаще всего) сделаете это явно — присвоите ему новое значение, скопируете, выполните метод и т. п. Таким образом, safe (или trackable) указатели на такие объекты имеют строго определённую семантику: они контролируют доступ к явно объявленному объекту, что бы потом с ним не происходило. А не к занимаемой объектом памяти (которая идёт как-бы прицепом).
А с объектами в контейнерах — хитрее. В контейнерах мухи — отдельно, котлеты — отдельно. Память распределяется аллокатором и потом контейнер обращается с ней так, как ему заблагорассудится. «Захотел» — разместил в ней один объект, «захотел» — переразместил другой. «Захотел» — вообще отволок объект в другое место. Таким образом выходит, что указатели, которые в первом случае контролировали доступ к объекту, который занимает какую-то память, в случае с контейнером начинают контролировать доступ к памяти, которую занимает какой-то объект. То есть несколько, гм, меняют свою семантику. Поэтому в случае контейнеров я бы вёл речь о стабильных итераторах, которые либо «указывают» на объект (элемент контейнера), на который были получены, либо явным образом инвалидируются и их невалидность можно проверить. С моей точки зрения, это более точно соответствует существующим контрактам контейнеров.
Здесь непонятно, что имелось в виду. Хотя для shared_ptr вообще SafePtr не нужен, просто для полноты?

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

Похоже вы что-то недописали в коде. или я что-то не понимаю, тут ничего не будет падать:

Стрелять должно в деструкторе owner-версии SafePtr, которого в примере действительно нет. :D

как я говорил, для shared_ptr такие указатели как SafePtr вообще бесполезны — вся функциональность и так есть из коробки.

Тут речь о консистентности. Когда клиент указывает в параметрах метода SafePtr — он не знает, с каким именно указателем этот метод будет вызван. То есть мы получаем некую абстракцию над указателями, которая гарантирует, что объект либо будет жив в момент доступа к нему, либо (если объект внезапно умирает) в программе будет не UB на доступе к разрушенному указателю, а вполне конкретное падение.

Эм… Разве сейчас именно такой принцип? Всё, что я об этом слышал, говорит скорее об обратном — копирование выполняется только в том случае, если нельзя выполнить перемещение. И, хоть это и не декларируется явно стандартом, но в целом требуется, чтобы после перемещения исходный объект оказался в валидном пустом состоянии.
странно, мне казалось все просто. какой объект разрушается, такие указатели и не валидны.
я понимаю, что семантически после удаления элемента в векторе, все последующие элементы перемещаются вправо, но по факту элементы валидны, кроме последнего. вы также ничем не гарантируете, что у объекта на стеке не вызвался оператор перемещения и теперь это «немного» другой объект.
Контейнер/не контейнер — без разницы. Гарантий нет.

Эм… Нет. Это немного разные вещи. Помещение safe-указателя в скоуп времени жизни указываемого объекта как раз таки гарантирует, что никто (ну, ок, почти никто :) ) не влезет и ничего не поменяет. То есть вы не можете с SafePtr на стековый объект снять копию и использовать её после выхода за скоуп указываемого объекта — у вас стрельнёт либо ассерт на локе, либо вылетит исключение. Есть кейсы с многопоточностью, когда какой-то поток может «сбоку» что-то там подправить в контейнере, но это уже типичный data race и надо лечить именно его.

Аналогично с контейнерами — ситуация, когда вы снимаете с элемента контейнера указатель или итератор, а потом этот элемент инвалидируете — в целом, ошибочная. То есть ваш код должен быть написан так, чтобы исключить такие вещи. А не помогать их «мягко» обойти. :) А приведённый в комментарии выше пример с кешем — там в map'е лучше сразу trackable-указатели хранить (или как-то так), чтобы дать клиенту гарантию, что удаление элемента из контейнера инвалидирует всё, что связанно именно с этим элементом.

У вас время жизни объекта ограничивается деструктором и оператором перемещения, как вы сможете реализовать свой SafePtr в таком случае?
Обязать клиента поддерживать эти инварианты?

Не совсем понял вопрос.

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity