Как пометить * стандартную библиотеку * функцию/метод как устаревшую (или полностью отключенную) в моем проекте?

Я пытаюсь каким-то образом отключить/пометить как устаревшую отвратительную перегрузку std::string::operator=(char) (которая по моему опыту используется только при ошибочном назначении целочисленного числа в строку и вызывает тонкую и трудную отслеживание ошибок).

Я пробовал:

  • явная специализация со статическим утверждением в нем

    #include <string>
    #include <type_traits>
    
    template<> std::basic_string<char> &std::basic_string<char>::operator=(char c) {
        static_assert(false, "Don't use this!");
    }
    

    который не работает, поскольку <string> уже выполняет явное инстанцирование std::string

  • атрибут [[deprecated]], примененный к аналогичному объявлению, как указано выше, в разных положениях; никакая позиция, которую я пытался, не давала никакого разумного результата;
  • =delete, который не работает по причинам, аналогичным приведенным выше;
  • Я думал об использовании утилит-линкера (в том же духе, в том же проекте у нас есть проверки времени выполнения при использовании stray setlocale с использованием компоновщика --wrap ld), но тот факт, что это шаблон и встроенный метод усложняет этот вопрос.

Теперь на вопросы:

  • существует стандартный метод, позволяющий каким-либо образом отключить (как и при использовании =delete) любую функцию или метод в стандартной библиотеке (читайте: в библиотеке, где вы не можете изменять объявления в заголовках)?
  • как указано выше, но вместо отключения добавить предупреждение (как это было бы с [[deprecated]]);
  • В противном случае стандартный метод, есть ли что-то g++-specific?
  • если нет "общего" (= применимо к любому методу, любому классу, любой функции,...) решения, есть ли что-то, что мы могли бы применить к этому конкретному случаю (= отключить метод класса шаблона, возможно даже конкретный экземпляр)?

Ответ 1

Вы можете использовать следующую опцию компилятора/компоновщика:

$ g++ -O0 test.cpp -Wl,--wrap=_ZNSsaSEc

Объяснение:

_ZNSsaSEc - это украшенное название вашей нарушающей функции:

$ echo _ZNSsaSEc | c++filt
std::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(char)

Параметр -Wl компилятора должен передать параметры компоновщику.

И опция компоновщика --wrap=<symbol> преобразует любую ссылку на данный символ в альтернативу __wrap_<symbol>. И поскольку вы (надеюсь) не определяете функцию с именем __wrap__ZNSsaSEc, вы получите хорошую ошибку компоновщика:

test.cpp:(.text+0x26): undefined reference to `__wrap__ZNSsaSEc'

И -O0 - отключить оптимизацию и запретить компилятору встраивать функцию. Учетная ссылка не будет работать, если есть вложение, как указано в комментарии @SergeBallesta.

Может быть, немного взломать, но эй, это работает!

Ответ 2

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

Уродливым способом (никогда не говори I советуем вам использовать его;-)) было бы использовать тот факт, что заголовки стандартной библиотеки - это просто текстовые файлы, поэтому вы можете легко их изменить в локальном развивающая среда. Возможно, менее плохим способом было бы установить папку, содержащую ссылки на исходные заголовки, за исключением измененного заголовка, и дать указание компилятору использовать эту папку для заголовков системы.

Таким образом, вы можете изменить все, что хотите, но... портативность и ремонтопригодность... Это действительно решение для отчаяния...

Ответ 3

Это clang++ specific, так как я не знаю, что эквивалентная функциональность вызывается в gnu toolchain. Это также несколько завышено.

Предложение Родриго об использовании компоновщика для замены символа отлично подходит для не-вложенных случаев. Если вы все время создаете на O0, это будет достаточно.

В противном случае инструментальная цепочка llvm (clang) предлагает удивительный контроль над конвейером оптимизации. Например, вы можете компилировать без оптимизаций, а затем запускать оптимизацию самостоятельно с помощью opt, а затем конвертировать в объектный файл.

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

Инструмент opt является расширяемым. Я не могу честно сказать, что это тривиально расширять, но процесс хорошо документирован. Вы можете написать пропуск компилятора, который предупреждает, когда будет видна ваша стандартная библиотечная функция. Конечный результат можно вызвать примерно так:

clang++ -c test.cpp -O0 --emit-llvm test.ll
opt --load DeprecationPass.so test.bc -o /dev/null
opt -O3 test.bc -o faster.ll
clang++ -c faster.bc -o test.o

Если вы уверены, что пользовательский пропуск является правильным (а не просто полезным), вы можете использовать один вызов opt. Вероятно, возможно передать флаги, чтобы выбрать через фронт clang, но это не сразу очевидно, как это сделать.

В целом, после предложения Rodrigo и иногда создания всего продукта на O0, вероятно, лучший план - но это захватывающе, что clang позволяет делать такие вещи.