Как я могу гарантировать, что никакой код не использует 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 и изменение исходного кода, вы действительно ничего не можете сделать.