Есть ли предупреждение GCC для использования символов из библиотеки C не через пространство имен std?
Рассмотрим следующий (багги) код С++:
#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return 0;
}
}
Этот код неисправен, потому что он вызывает abs
(значение ::abs
) вместо std::abs
. В зависимости от реализации ::abs
может не существовать, или это может быть C abs
, или это может быть набор перегрузки, включая версию для double
, например std::abs
.
С Clang on Linux, по крайней мере в моей среде, это второй вариант: C abs
. Это вызывает два предупреждения, даже если они явно не позволяют:
<source>:7:9: warning: using integer absolute value function 'abs' when argument is of floating point type [-Wabsolute-value]
if (abs(-0.75) != 0.75) {
^
<source>:7:9: note: use function 'std::abs' instead
if (abs(-0.75) != 0.75) {
^~~
std::abs
<source>:7:13: warning: implicit conversion from 'double' to 'int' changes value from -0.75 to 0 [-Wliteral-conversion]
if (abs(-0.75) != 0.75) {
~~~ ^~~~~
В GCC я получаю разные результаты в разных средах, и я еще не понял, какие детали окружающей среды актуальны. Однако более распространенным вариантом является то, что он вызывает функцию C abs
. Однако даже при -Wall -Wextra -pedantic
он не дает никаких предупреждений. Я могу заставить предупреждение с -Wfloat-conversion
, но это дает слишком много ложных срабатываний на остальной части моей кодовой базы (что, возможно, я должен исправить, но это другая проблема):
<source>: In function 'int main()':
<source>:7:18: warning: conversion to 'int' alters 'double' constant value [-Wfloat-conversion]
if (abs(-0.75) != 0.75) {
^
Есть ли способ получить предупреждение, когда я использую библиотечную функцию через глобальное пространство имен, когда версия в пространстве имен std
является перегрузкой?
Ответы
Ответ 1
Вот решение. Я не доволен этим, но это может сработать для вас:
namespace DontUseGlobalNameSpace {
// put all std functions here you want to catch
int abs(int x);
}
using namespace DontUseGlobalNameSpace;
Теперь, если вы используете abs()
без квалификации, вы получите ошибку "символ является неоднозначной".
Ответ 2
Это будет сложно. Заголовок GCC <cmath>
просто включает <math.h>
, #undefs
его макросы (на всякий случай) и определяет функции С++ как встроенные функции, которые используют некоторые идентификаторы из <math.h>
. Большинство функций на самом деле относятся к встроенным компиляторам: например, std::abs
определяется с помощью __builtin_abs
, а не ::abs
.
Так как <cmath>
и ваша "багги-программа" находятся в одной и той же единицы перевода, трудно понять, как можно разделить видимость: как встроенные функции в <cmath>
могут быть использованы для использования материалов <math.h>
в то время как ваш код не будет.
Ну, есть следующий способ: <cmath>
необходимо переписать, чтобы предоставить свои собственные объявления с локальным охватом для всего, что ему нужно, от <math.h>
и не включать этот заголовок.
Вместо этого мы можем подготовить заголовочный файл, который повторно объявляет функции, которые мы не хотим, с помощью __attribute__ ((deprecated))
:
// put the following and lots of others like it in a header:
extern "C" int abs(int) throw () __attribute__ ((deprecated));
#include <cmath>
#include <cstdlib>
#include <iostream>
int main() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return 0;
}
}
Сейчас:
$ g++ -Wall buggy.cc
buggy.cc: In function ‘int main()’:
buggy.cc:9:7: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^~~
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~
buggy.cc:9:16: warning: ‘int abs(int)’ is deprecated [-Wdeprecated-declarations]
if (abs(-0.75) != 0.75) {
^
In file included from /usr/include/c++/6/cstdlib:75:0,
from buggy.cc:4:
/usr/include/stdlib.h:735:12: note: declared here
extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
^~~
Предупреждение о компоновщике будет проще. Я попробовал это; проблема в том, что эта тестовая программа фактически не создает внешнюю ссылку на abs
(хотя есть #undef abs
in <cmath>
). Вызов встраивается и поэтому уклоняется от предупреждения компоновщика.
Update:
Следуя комментарию DanielH, я придумал уточнение трюка, который позволяет std::abs
, но блокирует abs
:
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
int abs(int) __attribute__ ((deprecated));
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}
Можно использовать простые пространства имен. Кроме того, нам не нужен атрибут deprecated
; мы можем просто объявить abs
как несовместимую функцию или не-функциональный идентификатор целиком:
#include <cmath>
#include <cstdlib>
#include <iostream>
namespace proj {
// shadowing declaration
class abs;
int fun() {
if (abs(-0.75) != 0.75) {
std::cout << "Math is broken!\n";
return 1;
} else {
return std::abs(-1); // must be allowed
}
}
}
int main() {
return proj::fun();
}
$ g++ -std=c++98 -Wall buggy.cc -o buggy
buggy.cc: In function ‘int proj::fun()’:
buggy.cc:10:18: error: invalid use of incomplete type ‘class proj::abs’
if (abs(-0.75) != 0.75) {
^
buggy.cc:7:9: note: forward declaration of ‘class proj::abs’
class abs;
^~~
buggy.cc:16:3: warning: control reaches end of non-void function [-Wreturn-type]
}
^
При таком подходе нам просто нужен список имен и дамп их в некоторый заголовок, который обеспечивает это:
int abs, fabs, ...; // shadow all of these as non-functions
Я использовал -stdc++98
в командной строке g++
, чтобы подчеркнуть, что это просто старая школьная С++ namespace
семантика на работе.
Ответ 3
Этот код позволит вам определить, существует ли ловушка в конкретной среде:
double (*)(double) = &::abs; // fails if you haven't included math.h, possibly via cmath
Но это не поможет вам определить места, в которые вы попадаете в ловушку.