Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a GCC warning for using symbols from the C library not through namespace std?

Consider the following (buggy) C++ code:

#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;
    }
}

This code is buggy because it calls abs (meaning ::abs) instead of std::abs. Depending on the implementation, ::abs might not exist, or it might be the C abs, or it might be an overload set including a version for double, like std::abs is.

With Clang on Linux, at least in my environment, it turns out to be the second option: C abs. This provokes two warnings, even without explicitly enabling any:

<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) {
        ~~~ ^~~~~

On GCC, I get different results in different environments and I haven’t yet figured out what details of the environment are relevant. The more common option, though, is also that it calls the C abs function. However, even with -Wall -Wextra -pedantic, it gives no warnings. I can force a warning with -Wfloat-conversion, but that gives too many false positives on the rest of my codebase (which perhaps I should fix, but that’s a different issue):

<source>: In function 'int main()':
<source>:7:18: warning: conversion to 'int' alters 'double' constant value [-Wfloat-conversion]
     if (abs(-0.75) != 0.75) {
                  ^

Is there a way to get a warning whenever I use a library function through the global namespace, when the version in namespace std is an overload?

like image 842
Daniel H Avatar asked Jul 13 '17 17:07

Daniel H


People also ask

Which option of GCC inhibit all warning messages?

If -Wfatal-errors is also specified, then -Wfatal-errors takes precedence over this option. Inhibit all warning messages. Make all warnings into errors.

Which GCC flag is used to enable all compiler warnings?

gcc -Wall enables all compiler's warning messages. This option should always be used, in order to generate better code.

Which option can be used to display compiler warnings?

You can use a #pragma warning directive to control the level of warning that's reported at compile time in specific source files. Warning pragma directives in source code are unaffected by the /w option.

How do I enable warnings in GCC?

GCC 4.3+ now has -Q --help=warnings , and you can even specify --help=warnings,C to just print out the C related warnings.

How to suppress the warning that X is always initialized in GCC?

If the value of y is always 1, 2 or 3, then x is always initialized, but GCC doesn’t know this. To suppress the warning, you need to provide a default case with assert(0) or similar code. This option also warns when a non-volatile automatic variable might be changed by a call to longjmp. The compiler sees only the calls to setjmp.

How do I eliminate the warning-wparentheses in GCC for IF statements?

When there is the potential for this confusion, GCC issues a warning when this flag is specified. To eliminate the warning, add explicit braces around the innermost if statement so there is no way the else can belong to the enclosing if. The resulting code looks like this: This warning is enabled by -Wparentheses .

Why does GCC issue warnings when I use a common variable?

To help detect accidental misuses of such arrays GCC issues warnings unless it can prove that the use is safe. See Common Variable Attributes . Warn for cases where adding an attribute may be beneficial.

How do I remove a GCC warning when a flag is specified?

When there is the potential for this confusion, GCC issues a warning when this flag is specified. To eliminate the warning, add explicit braces around the innermost if statement so there is no way the else can belong to the enclosing if. The resulting code looks like this:


2 Answers

Here's a solution. I'm not happy with it, but it might work for you:

namespace DontUseGlobalNameSpace {
// put all std functions here you want to catch
int abs(int x);
}
using namespace DontUseGlobalNameSpace;

Now, if you use abs() without qualification, you'll get a "symbol is ambiguous" error.

like image 113
geza Avatar answered Sep 29 '22 11:09

geza


This is going to be difficult. The GCC <cmath> header simply includes <math.h>, #undefs its macros (just in case) and defines the C++ functions as inline functions which make some use of identifiers from <math.h>. Most of the functions in fact refer to compiler builtins: for instance, std::abs is defined using __builtin_abs and not ::abs.

Since <cmath> and your "buggy program" are all in the same translation unit, it's hard to see how the visibility could be separated: how the inline functions in <cmath> could be allowed to use <math.h> stuff, while your code wouldn't.

Well, there is the following way: <cmath> would have to be rewritten to provide its own locally scoped declarations for anything that it needs from <math.h> and not actually include that header.

What we can do instead is prepare a header file which re-declares the functions we don't want, with an __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;
  }
}

Now:

$ 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;
            ^~~

A linker warning would be simpler. I tried that; the problem is that this test program doesn't actually generate an external reference to abs (even though there is an #undef abs in <cmath>). The call is being inlined, and so evades the linker warning.

Update:

Following up DanielH's comment, I have come up with a refinement of the trick which allows std::abs but blocks 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();
}

Simple namespaces can be used. Also, we don't need the deprecated attribute; we can just declare abs as an incompatible function, or a non-function identifier entirely:

#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]
   }
   ^

With this approach, we just need a list of names and dump them into some header that provides this:

int abs, fabs, ...; // shadow all of these as non-functions

I used -stdc++98 in the g++ command line to emphasizes that this is just old school C++ namespace semantics at work.

like image 43
Kaz Avatar answered Sep 29 '22 11:09

Kaz