Warnings or errors for C++ implicit conversion of primitives
-
11-10-2019 - |
Question
I've done some heavy refactoring of some C++ code, and discovered numerous bugs arising from implicit conversions that I'm not aware of.
Example
struct A *a();
bool b() {
return a();
}
void c() {
int64_t const d(b());
}
Issues
- In
b
, the return type ofa
is silently cast tobool
. - In
c
, the value returned fromb
is silently promoted toint64_t
.
Question
How can I receive warnings or errors for the implicit conversion between primitive types?
Note
- The use of
-Wconversion
seems to only pick up several arbitrary conversions unrelated to the example above. BOOST_STRONG_TYPEDEF
is not an option (my types need to be PODs, as they're used in disk structures).- C is also of interest, however this problem pertains to a C++ code base.
Solution
In the C++ programming language, 3rd edition, appendix C.6, namely "Implicit Type Conversion", Bjarne Stroustrup classifies conversions as promotions and conversions: the first ones "preserve values" (that's your case 2), the second ones doesn't (case 1).
About conversions, he says that "The fundamental types can be converted into each other in a bewildering number of ways. In my opinion, too many conversions are allowed." and "A compiler can warn about many questionable conversions. Fortunately, many compilers actually do."
promotions on the other side are safe, and it seems like a compiler is not supposed to give a warning for them.
Compiler warnings are usually not mandatory. Usually in the C++ drafts and final ANSI documents it is reported that "implementers should issue a warning" where suggested: you can check it yourself for further information if needed.
EDITED: added C++11 note:
In The C++ programming language, 4th edition, the appendix of the 3rd edition has been reported and extended as section 10.5, "Implicit Type Conversion" again.
Being the previous considerations the same, C++11 more precisely define "narrowing conversions" and adds up the {}-initializer notation (6.3.5), with which truncations lead to a compilation error.
OTHER TIPS
If you are using gcc have you tried -Wall -Wextra basically check this page
http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
If it is not GCC please post the compiler details.
Microsoft Visual C++ would give a warning about the narrowing conversion from A*
to bool
.
Promotion on the other hand is not a "dangerous" conversion, because it's impossible to lose data.
EDIT: Demonstration
C:\Users\Ben>copy con test.cpp
bool f( void ) { return new int(); }
^Z
1 file(s) copied.
C:\Users\Ben>cl /c /W4 test.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.cpp
test.cpp(1) : warning C4800: 'int *' : forcing value to bool 'true' or 'false' (
performance warning)
As far as I understand, you cannot control implicit conversion between primitive types: it's mandated by the standard and any compliant compiler will just perform it silently.
Are you sure an approach like BOOST_STRONG_TYPEDEF won't work in your problem? A class with no virtual member functions and just one primitive data member is basically nothing more than a POD data type. You can just follow the same approach and only allow conversion to the base primitive type; example:
#include <iostream>
#include <stdexcept>
struct controlled_int {
// allow creation from int
controlled_int(int x) : value_(x) { };
controlled_int& operator=(int x) { value_ = x; return *this; };
// disallow assignment from bool; you might want to use BOOST_STATIC_ASSERT instead
controlled_int& operator=(bool b) { throw std::logic_error("Invalid assignment of bool to controlled_int"); return *this; };
// creation from bool shouldn't happen silently
explicit controlled_int(bool b) : value_(b) { };
// conversion to int is allowed
operator int() { return value_; };
// conversion to bool errors out; you might want to use BOOST_STATIC_ASSERT instead
operator bool() { throw std::logic_error("Invalid conversion of controlled_int to bool"); };
private:
int value_;
};
int main()
{
controlled_int a(42);
// This errors out:
// bool b = a;
// This gives an error as well:
//a = true;
std::cout << "Size of controlled_int: " << sizeof(a) << std::endl;
std::cout << "Size of int: " << sizeof(int) << std::endl;
return 0;
}
You can use one of the available static analysis tools, programs like clint or C++ equivalents, or one of the commercially available tools. Many of these tools can pick out problematic implicit conversions.
Write a custom clang plugin to diagnose your problems. We do a lot of this in the LibreOffice code. Come browse our source if you want inspiration at http://cgit.freedesktop.org/libreoffice/core/tree/compilerplugins/clang