LINUX.ORG.RU

Насильное ограничение подмножества C++ в проекте

 ,


0

6

Привет, ЛОР!

Скажи, а какие есть рецепты для сабжа? Допустим, я не хочу, чтобы в проекте использовались сырые указатели, new/delete, вызовы из cstdlib и прочие штуки, которым в современном C++ есть более кошерная замена. Есть ли возможность завалить сборку, если в коде такое встретилось? В идеале, я представляю себе некую утилиту, которая анализирует исходники и выдаёт предупреждение, если встретилось что-то из заданного ей списка.

Спасибо, ЛОР.

Ответ на: комментарий от LINUX-ORG-RU

О нет! Таких костылей я делать не хочу. И потом, как ты скриптом проверишь использование сырых указателей, например? С плюсовым-то синтаксисом.

hateyoufeel ★★★★★
() автор топика

Я делал плагин для clang-tidy, который искал в исходниках вызов определённой функции и выдавал ворнинг. Но разбираться в clang AST тот ещё гемор. Если не готов на такой подвиг, то пробуй грепать.

ox55ff ★★★★★
()
Ответ на: комментарий от LINUX-ORG-RU

В GCC есть MELT, я про него только краем уха слышал, не знаю может не то, но вроде позволяет препарировать код :3

Зашёл сюда, там пишут, что доки по новому адресу лежат. А новый адрес уже увели и там какая-то реклама, к GCC отношения не имеющая.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Вот вродя https://arxiv.org/pdf/1109.0779.pdf фик знает насколько актуально.
Можно по всяким примерчикам порыть типа

Просто понять это оно то что надо или не совсем. Я не вникал, мне сложна.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 2)
Ответ на: комментарий от LINUX-ORG-RU

Вот вродяhttps://arxiv.org/pdf/1109.0779.pdfфик знает насколько актуально.

Можно по всяким примерчикам порыть типа

Дядя, оно дохлое. Если разрабам GCC настолько насрать, что они ссылку на доки не могут поправить при том, что у них домен давно увели, то использовать этот код как минимум просто боязно.

Я вообще на GCC не очень хочу завязываться нигде. LLVM выглядит куда живее.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Да это не так уж сложно в варианте one shot, поддержка может быть сложнее, но с другой стороны - зависит от требований, вряд-ли они когда-нибудь сломают парсинг дереференса сырого указателя.

Stil ★★★★★
()

Если найдешь утилиту, которая умеется искать использование сырых указателей, то можно вызов этой утилиты добавить в этапы сборки cmake через команду add_custom_commnad и тогда сборка будет завершаться ошибкой, если утилита не отработала успешно.

rumgot ★★★★★
()
Ответ на: комментарий от ac130kz

std::filesystem

По факту - сталкиваться не приходилось.

std::make_unique, std::make_shared, и т.д.

Вы уверены что до конца понимаете зачем make_shared ввели? На самом деле - полезняшка. А вот make_unique - от лукавого - ничего в нём нет такого волшебного.

bugfixer ★★★★★
()

Короче в embedded средах бывает возможность включать статические анализаторы на соответствие какой-нибудь MICRA, в том числе на отдельные пункты. Там вот эти ваши желания можно реализовать настроив сборку проекта соответствующим образом…

Если проект высокоуровневый (операционная система есть и вот это вот смузихлебное всё), то посмотрите на статические анализаторы кода (например, pvs), они обычно с такими задачами справляются. Но заваливать сборку (наверное) не будут. Можно просто анализировать код перед сборкой, вопросом автоматизации сборки и старическим анализом в высокоуровневом ПО я не задавался если честно.

aiqu6Ait ★★★★
()
Последнее исправление: aiqu6Ait (всего исправлений: 1)
Ответ на: комментарий от ac130kz

Ну да, ничего волшебного, просто выпиливаем raw указатели

Nothing is faster than raw pointers.

хочет ТС

Кмк - товарищ гораздо умнее чем хочет казаться. Это из моих многолетних наблюдений.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Nothing is faster than raw pointers.

Главное – веровать!

Кмк - товарищ гораздо умнее чем хочет казаться. Это из моих многолетних наблюдений.

Это всё потому что у меня звёздочек больше?

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)

представляю себе некую утилиту, которая анализирует исходники и выдаёт предупреждение, если встретилось что-то из заданного ей списка.

Написать правило или аддон для cppcheck.

dataman ★★★★★
()
Ответ на: комментарий от ac130kz

std::make_unique, std::make_shared

Не помогут, если нужно создавать экземпляры приватных классов. Тут как раз голый new и нужен.

Кроме того, голый new нужен при placement new.

Это я к тому, что в 95% случаев за глаза хватит std::make_unique и, вероятно, в 90% случаев хватит std::make_shared (но тут нужно смотреть насколько «тяжелые» объекты создаются через std::make_shared и как часто случаются случаи, когда strong-ссылок на объект уже нет, а weak-ссылки остаются). Но чем более нетривиальная кодовая база (даже на modern C++, где modern уже посвежее C++14), тем больше шансов, что голые new потребуются. А там и до голых delete недалеко, хотя для голых delete места осталось совсем немного.

eao197 ★★★★★
()

чтобы в проекте использовались сырые указатели

Это не обязательно, если принять соглашение, что сырые указатели не владеют ресурсом, как написано здесь: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-ptr

new/delete

Как вариант ты можешь написать coding guidelines, или просто скопировать из https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-newdelete

вызовы из cstdlib

Здесь есть такая техника: делаешь заголовочный файл disallowed.h, где пишешь, например для gets:

#define gets(x) gets_disallowed

И компилируешь свой код с g++ -include disallowed.h. И тогда у тебя при использовании gets вылетает ошибка:

disallowed.cpp:1:17: error: ‘gets_disallowed’ was not declared in this scope
    1 | #define gets(x) gets_disallowed
      |                 ^~~~~~~~~~~~~~~
disallowed.cpp:8:5: note: in expansion of macro ‘gets’
    8 |     gets(s);
      |     ^~~~
rupert ★★★★★
()
Ответ на: комментарий от eao197

А если не секрет, что так? (Вопрос без сарказма).

Rust как язык сам по себе вполне норм. Некоторых вещей, конечно, не хватает (higher-kinded types, например). Плюс, async натянут как сова на глобус, но с этим тоже можно кое-как жить.

Rust как экосистема – это ад и холокост, от вида которого хочется рвать волосы на жопе и бегать, обливаясь скипидаром. Типичный проект может тащить 500 и больше зависимостей, собираться по полчаса и сломаться, потому что так выйдет, что две разные зависимости будут тащить разные версии какой-то третьей библиотеки.

Плюс, половина программистов на Rust страдают от чудовищного отсутствия культуры разработки. Зачем проверять возвращаемое значение, если можно просто дёрнуть .unwrap()? Ну и что что падает? Нормальное сообщение об ошибке захотел? Нахрен иди! И хорошо ещё если это вылезет в самом твоём проекте, а не в какой-нибудь библиотеке глубоко в графе зависимостей. Короче, вылезают те же грабли, что в JS+NPM: всё всегда сломано, релизы годовалой давности сегодня уже не собираются, и никто не знает что делать.

hateyoufeel ★★★★★
() автор топика

Почему ни одной штуки про препроцессор?

$ g++ -Dnew=hate main.cpp 
main.cpp: In function «int main()»:
<command-line>: ошибка: нет декларации «hate» в этой области видимости
main.cpp:1:20: замечание: в расширении макроса «new»
    1 | int main(){return *new int(0);}
      |                    ^~~
main.cpp:1:24: ошибка: expected «;» before «int»
    1 | int main(){return *new int(0);}
      |                        ^~~

ratvier ★★
()
Ответ на: комментарий от hateyoufeel

Слишком затратно. Я думал, вдруг есть что-то готовое.

Так и задача непростая, если подумать. Будет куча исключений из правил, чтобы твой гайдлайн вписался в реальный мир. Вряд ли найдётся что-то лучше clang-tidy, потому что он решает ровно эту проблему «фреймворка для линтеров».

Из менее подходящих тулзов в голову приходит:

  1. Проприетарный Github CodeQL и подобные ему query language. Сам не использовал, но вдруг;
  2. Facebook Infer
  3. Tree-sitter: https://siraben.dev/2022/03/22/tree-sitter-linter.html (но парсинг C++ не очень в реальной жизни).
snizovtsev ★★★★★
()
Ответ на: комментарий от snizovtsev

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

Большинство таких утилит позволяют разрешить локальное использование запрещённых фич через специальные комменты.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от eao197

создавать экземпляры приватных классов

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

ac130kz ★★
()
Ответ на: комментарий от hateyoufeel

Ну ты даже не определился – нужно ли для твоих проверок раскрывать хедеры, макрос, инстанцировать шаблоны. Если нужно, то «локальность» уже не так очевидна. И да, если совместить требования «фреймворк» + «линтер» + «умеет в спец комменты» – то наверное ничего, кроме clang-tidy не останется.

snizovtsev ★★★★★
()
Ответ на: комментарий от ac130kz

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

Аргументация какая-нибудь будет?

Например, что криминального вы найдете здесь:

class some_big_class
{
  class nested_private_class { ... };

  std::unique_ptr<nested_private_class> helper_;
  ...
public:
  some_big_class()
    : helper_{ new nested_private_class{...} }
  {}
  ...
};

Тут ведь ничего сложного, банальный pImpl выглядит почти что так же.

eao197 ★★★★★
()
Ответ на: комментарий от snizovtsev

Ну ты даже не определился

Кто тебе сказал?

нужно ли для твоих проверок раскрывать хедеры, макрос

Учитывая, какой ад творится в библиотечных заголовочных файлах, это будет очень тупо.

Но вообще, заголовки должны умереть. Я всё надеюсь, что скоро можно будет использовать модули во всю.

если совместить требования «фреймворк» + «линтер» + «умеет в спец комменты» – то наверное ничего, кроме clang-tidy не останется.

Да, я так и понял уже.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от ac130kz

Есть такой паттерн. Но тогда вы свой приватный класс, фактически, выставляете наружу, а тип-тег является всего лишь костылем, чтобы обойти ограничение.

Т.е. мы боимся голого new, потому что это страх и ужос, поэтому готовы выставлять наружу кишки и приделывать костыль только для того, чтобы можно было вызвать std::make_unique.

Ну так себе размен.

При том, что опасность представляет не голый new (который запросто может быть и placement new), а голый владеющий указатель (который, кстати говоря, может получаться вовсе и не из-за new). Но бороться нужно с new.

OK.jpg ;)

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Не помогут, если нужно создавать экземпляры приватных классов. Тут как раз голый new и нужен.

Кроме того, голый new нужен при placement new.

В принципе правило на запрет new может разрешать подобные случаи. Ну и для отдельных случаев можно отключать проверку комментариями вида // disable-next-line: explicit-new.

static_lab ★★★★★
()
Ответ на: комментарий от MKuznetsov

Лучше наоборот, Orthodox C++

https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b

Нет, не лучше. Наследие однобуквенного убогонького недоязычка – это то, от чего C++ лучше избавить.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от bugfixer

make_shared нужно с умом использовать. И понимать, что там под капотом происходит, а то уже видел людей удивляющихся: у меня больше нет инстансов shared_prt, почему память не воротается… А то, что у него weak_ptr весь блок держит, он и не подумал. При этом у них освобождение памяти было сильно с вызовом деструктора ассоциировано :)

hatred ★★★
()
Ответ на: комментарий от eao197

Но тогда вы свой приватный класс, фактически, выставляете наружу…
…готовы выставлять наружу кишки

public от private в C++ не так много различий, чтобы считать, что public классы/функции/переменные выставленны наружу, а private не выставленны. private буквально ото всюду видно, откуда видно и public.

rupert ★★★★★
()
Ответ на: комментарий от rupert

public от private в C++ не так много различий, чтобы считать, что public классы/функции/переменные выставленны наружу, а private не выставленны. private буквально ото всюду видно, откуда видно и public.

Когда у вас есть строго:

class outer {
  class inner {...}
};

то сразу понятно, что inner – это часть внутренней кухни outer-а.

Когда есть что-то вроде:

class outer {
  class inner_tag {};

public:
  class inner {
  public:
    inner(inner_tag);
  };

private:
  ...
};

то это уже озадачивает.

А еще интереснее становится, если у inner должен быть конструктор по умолчанию. Ну вот должен быть и все.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Так можно просто добавить комментарий public: class inner // private, no really. Такого рода приёмчики - хлеб и соль C++: как извратиться с синтаксисом, чтобы достичь то, о чём раньше никто не догадывался.

Но, честно говоря, для make_unique я бы такой фигней заниматься не стал. А вот для make_shared наверное стал бы, потому что там на одну аллокацию меньше.

rupert ★★★★★
()
Последнее исправление: rupert (всего исправлений: 1)