Как я могу гарантировать, что никакой код не использует API?
Я хочу запретить использование iostreams в базе кода, которую я имею (по разным причинам). Есть ли способ проверить файлы символов или заставить компилятор испускать ошибку при использовании этого API?
Ответы
Ответ 1
Простым подходом является реализация dummy iostream
, которая ничего не делает, кроме как выдает ошибку времени компиляции.
В следующем примере предполагается GCC toolchain - я думаю, что процесс похож на другие компиляторы.
Сначала создайте файл фиктивного iostream:
#error 'Use of iostream is prohibited'
Некоторые примеры кода для демонстрации:
#include <iostream>
int main (int argc, char** argv) {
std::cout << "foo!";
return 0;
}
Скомпилируйте следующим образом (предполагая, что манекен iostream
и main.cpp
находятся в рабочем каталоге):
g++ -I. main.cpp
Сбой компиляции со следующими ошибками:
In file included from main.cpp:2:0:
./iostream:1:2: error: #error 'Use of iostream is prohibited'
main.cpp: In function 'int main(int, char**)':
main.cpp:4:2: error: 'cout' is not a member of 'std'
Добавленный бонус: символы, обычно объявленные в этом файле (например, cout
здесь), являются undefined и поэтому также помечены в компиляторе. Таким образом, вы также получаете указатели на то, где вы используете свой запрещенный API.
UPDATE: Инструкции для Visual С++ 2012.
Как отмечает @RaymondChen в комментариях ниже, решение, адаптированное к Visual С++, скорее всего, более полезно для OP. Таким образом, ниже описывается процесс, который я прошел, чтобы достичь того же, что и выше в Visual С++ 2012.
Сначала создайте новый проект консоли, используя приведенный выше код на С++. Также создайте фиктивный заголовок iostream, который я описал выше, и поместите его в каталог где-то легко найти (я поместил мой в основной исходный каталог проекта).
Теперь в обозревателе решений щелкните правой кнопкой мыши по проекту node и выберите "Свойства" в раскрывающемся списке. В появившемся диалоговом окне выберите "Каталоги VС++" из дерева слева. Подготовьте каталог, содержащий файл фиктивного iostream, в список включенных каталогов, который отображается справа, отделенный от других каталогов точкой с запятой:
![Adding new path to include directories list]()
Здесь мой проект был вызван TestApp1, и я просто добавил его основной каталог в $(IncludePath)
, который уже был там. Обратите внимание, что важно добавить, а не добавлять: порядок каталогов в списке определяет порядок поиска, поэтому, если перед вашим настраиваемым каталогом появится $(IncludePath)
, заголовок системы будет использоваться в предпочтении вашего фиктивного заголовка - не то, что вы хотите.
Нажмите "ОК" и перестройте проект.
Для меня это привело к следующим ошибкам в консоли VС++ (слегка отредактирован для краткости):
error C1189: #error : 'Use of iostream is prohibited'
IntelliSense: #error directive: 'Use of iostream is prohibited'
IntelliSense: namespace "std" has no member "cout"
Обратите внимание, что IntelliSense также подбирает (теперь) незаконное использование cout
- оно выделяется с меткой ошибки в редакторе.
Ответ 2
Это неприятный взлом, но он должен работать.
Стандарт C (и, следовательно, стандарт С++) позволяет использовать токены препроцессора в директивах #include
. Это также известно как "вычисленный включает".
Таким образом, добавление чего-то вроде -Diostream
в CFLAGS
внутри вашего файла makefile (или параметров компилятора в настройках вашего IDE-проекта) должно надежно разорвать сборку, если кто-то пытается использовать iostream.
Конечно, с пустым макросом сообщение об ошибке не будет очень информативным, но вместо этого вы можете использовать что-то вроде -Diostream=DUDE_DONT_USE_IOSTREAM
, которое будет показывать ошибку, например: DUDE_DONT_USE_IOSTREAM: file not found
.
Это также то, что вы можете отключить снова без особых хлопот, если позже передумаете. Просто удалите вариант сборки.
Ответ 3
Ваша идея проверять файлы символов возможна и очень реалистична. virtual ~ios_base();
- это единственный метод, который наследует все потоки и который не может быть легко встроен, поскольку он виртуальный и нетривиальный. Поэтому его присутствие в объектном файле является очень сильным признаком использования IOstream.
Ответ 4
В дополнение к методу поддержки компилятора, упомянутому Mac, вы можете использовать общие функции поиска. Например (предположим, что zsh shell - для bash не имеет **
, а в Windows вам нужно найти, как это сделать с помощью Powershell):
# Find all mentioning on `iostream` `cin` in all files ending in cc in all subdirectories of current directory
grep iostream **/*.c
grep cin **/*.cc
Если вы не хотите/не можете использовать командную строку, вы можете использовать свой любимый редактор и искать нежелательные символы.
Я обычно объединяю оба метода:
- Компиляция, особенно большого проекта с большим количеством шаблонов, медленна, когда поиск выполняется быстро, поэтому вы более продуктивны при поиске.
- С другой стороны, поиск работает неточно и может что-то пропустить. Поэтому я бы использовал трюки заголовков, чтобы проверить решение, сделанное на предыдущем шаге.
- В качестве окончательной проверки вы можете искать символы после компиляции. Это особенно полезно, если вы компилируете без оптимизации. Вы можете использовать
objdump
или аналогичный (в зависимости от платформы) и следить за импортированным символом (это работает, если вы не указали ссылку статически на что-то, используя iostreams).
Ответ 5
Нет, совсем нет. Для очень ограниченного подмножества вы можете предоставить свои собственные определения, в результате чего компоновщик ошибется в дубликатах. Тем не менее, это будет поведение undefined. И хорошая часть - это шаблоны, которые не восприимчивы к этому. Не делая резких действий, таких как удаление заголовка iostream или использование компилятора Clang и изменение исходного кода, вы действительно ничего не можете сделать.