Перейти к содержанию
    

Уникальность идентификаторов include guards

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

И если проблему с выбором конкретного файла решить весьма просто - на уровне синтаксиса #include "" или <> (при необходимости еще и с указанием полного пути), то вот из-за одинаковых именований защитных макросов последующие по иерархии подключений файлы не включат свое содержимое. Понятно, что во всем проекте желательно не использовать одинаковые имена заголовков, но это "желательно" иногда не возможно не нарушить.

Подправлять имя файла или его защитные макросы "по случаю" не очень хочется, т.к. иногда у меня структура папок немного разъезжается, да и мало ли что...

Как пример - у FreeRTOS есть queue.h с его QUEUE_H, projdefs.h с его PROJDEFS_H. Я тоже люблю так кратко именовать файлы и их защитников.

Вот только разработчики RTOS пишут не сильно думая об остальном наборе исходников у юзера, а юзеру (мне) уже нужно думать об уникальности защитников.

Есть ли в препроцессоре C/C++ возможность клеить имя защитного макроса на основе текущей директории файла?

Конкатенация ## для #ifndef не работает, поэтому напрямую не получится.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для конкретно этой задачи есть #pragma once. Вроде бы обещают, что она даже отличает один и тот же файл, доступный из разных мест по символическим ссылкам с разными именами от другого файла с таким же именем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, Сергей Борщ сказал:

Для конкретно этой задачи есть #pragma once. Вроде бы обещают, что она даже отличает один и тот же файл, доступный из разных мест по символической ссылке от другого файла с таким же именем.

Вроде да, но узнал я вот, что это не стандартная для реализаций компиляторов штука, ее поведение не строго определено. Даже ARM в описании на свой компилятор убедительно просит не злоупотреблять:vava:

Не уж то придется писать пре-билд скрипт, который пройдется по всем директориям проекта и переименует у заголовочников их защитников?:to_take_umbrage:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

51 минуту назад, EdgeAligned сказал:

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

Это у некоторых сред разработки, наверное, так. У меня Keil, но это даже не столь важно, ибо все файлы (свои) я создаю с нуля сам, отдельно от какой-то среды.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Например, касательно темы шаблон файла имеет такие строки для автогенерации:

${filecomment}
#ifndef ${include_guard_symbol}
#define ${include_guard_symbol}
....
#endif /* ${include_guard_symbol} */

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, EdgeAligned сказал:

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

Например, касательно темы шаблон файла имеет такие строки для автогенерации:

${filecomment}
#ifndef ${include_guard_symbol}
#define ${include_guard_symbol}
....
#endif /* ${include_guard_symbol} */

А автогенерация на каком уровне? На уровне среды - т.е. перед билдом запускается некий скрпипт, который проходится по каталогам исходников и "редактиурет" их?

А как же тогда отслеживать изменения в системах контроля версий?

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет, не перед билдом, а в процессе создания файла. Там, где вводится имя нового файла, есть список с шаблонами файлов. Так же, сама среда отслеживает переименования файлов и папок и вносит изменения в include_guard_symbol уже созданных файлов. И даже более того - изменяет пути в прописанных инклюдах, да и вообще поддерживает правильный рефакторинг кода. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 минут назад, EdgeAligned сказал:

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

Какая среда? Я не понимаю, в каком виде у Вас лежит на диске само содержимое файла .h? Можете пример привести?
 

Цитата

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

Это для меня лишнее, т.к. не думаю, что эта среда правильно все пропишет (имею в виду так, как это задумываю я).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Среда, основанная на Эклипсе 🙂 Раньше она называлась Atollic, сейчас называется CubeIDE.

Файлы .c, .cpp, .h, .hpp лежат на диске в самом обычном виде, их можноп ереносить куда угодно и использовать отдельно. Но сам проект содержит конфиг-файл, через который среда и следит за всеми используемыми файлами, а рефакторинг кода проводится самой средой разработки.

Ну а рефакторинг кода - это полезная весчь, среда разработки следит за изменениями, которые вы вносите, и изменяет синхронно все вхождения элементов, которые вы изменяете. Пример - это переименование переменных или фукнций, переименования файлов/папок или перемещения их в другие папки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Короче, на ум приходит написать скрипт (на каком-либо языке, да хоть на нативных средствах винды - "батник"), который запускать перед компиляцией проекта. Вызывать скрипты до и после компиляции умеют вообще все системы сборки и IDE уж точно. Так вот, этот скрипт пробежится по всем директориям с исходниками проекта, найдет все заголовочные файлы и скопирует во временную папку TempHeaders рядом с проектом, налету подменяя идентификатор защитного макроса на более длинный, основанный на реальном пути к файлу. Т.е. исходные файлы не будут физически затронуты скриптом. А вот include path среды настроить на эту временную папку TempHeaders и подтягивать заголовки оттуда! Идеально!:blush:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Интересно еще вот что (для ознакомления с опытом тутошних коллег)

1. Насколько глубоки директории ваших проектов? Т.е. разбит ли проект на кучку осмысленных папок? Или все почти в одной куче?
2. Если директорий много (проект хорошо логически разбит), есть ли какие-то правила, какие из этих директорий вносить в список include path среды?

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

// вместо
#include "platform/target/basic/types.h"

// писать
#include "types.h"

нужно каждую папку и каждую подпапку (вообще все места, где есть заголовочные файлы) вносить в список поисковых include path. В чем плюс: не нужно писать длинных путей #include "...". Минус же в том, что если директории проекта реально развесистые, задолбаешься вносить все эти пути в include path в свойствах проекта.

С другой стороны, если писать все эти относительные пути (относительно, например, самой "верхней" папки проекта), эти #include "..." будут весьма длинными, да и не особо переносимыми, т.к. при перетаскивании каких-то заголовков в другие папки проект перестанет собираться. Тоже такое себе. Зато если в исходник надо будет подключить несколько заголовочников с одинаковыми именами (но разным содержимым, т.к. они в разных директориях и относятся к разным вещам), никакой путаницы - по пути сразу все понятно что подключается.

Может есть какая-то серебряная пуля - в идеале - простое #include "types.h" ищет этот самый types.h в том же каталоге, в котором лежит файл с этой строчкой #include "types.h" (стандартное поведение большинства компиляторов), ну а если надо указать какой-то конкретный файл в длинной директории, эту директорию не надо было писать полностью: например #include "platform/../types.h"?

Глянул в репозиторий линукса - там папка include вообще отдельная (мне так не нравится ибо мухи вместе с котлетами перемешаны), а большинство исходников начинаются с #include <linux/file.h>. Т.е. такое чувство, что список путей поиска включений у линукса весьма маленький и основной каталог как раз linux/include/linux где все, повторюсь, в кучу свалено.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В своем коде не более одного уровня подкаталогов. Компилятору передаю "-I." чтобы отмерял все пути от корня проекта, и не нужно было по разному подключать один и тот же заголовок из разных мест.

Бывают исключения когда встроен какой-то сторонний код, путь получается длиннее, но за счет include path не сокращаю. Могу исправить этот сторонний код если совсем плохо вписывается. Например freertos у меня это одна плоская директория с файлами, оригинальную структуру я не сохранял, и еще поправил заголовки, так, чтобы подключать в своем коде только один а в нем уже включены все queue.h и остальное.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Понятно:russian_ru: ИМХО, править что-то в исходниках стороннего middleware мне видится не очень правильным подходом...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 часов назад, Arlleex сказал:

нужно каждую папку и каждую подпапку (вообще все места, где есть заголовочные файлы) вносить в список поисковых include path. В чем плюс: не нужно писать длинных путей #include "...". Минус же в том, что если директории проекта реально развесистые, задолбаешься вносить все эти пути в include path в свойствах проекта.

Реально развесистыми могут быть include-директории стороннего ПО, например, SDK, к тому же SDK иногда обновляется, и после обновления SDK что-то может поломаться в сборке проекта и нужно откатить всё обратно на старый SDK, и т. д. Выручает построение include-путей через макросы типа $(SDK_DIR)/include;$(SDK_DIR)/drivers/include

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...