In previous posts I've described how to build Clang on RHEL. This allows us to have access to an alternative compiler to help catch errors early. In this and some upcoming blog posts I'm going to go over some of the errors that using multiple compilers has helped me catch.
Hidden Overloads
Below is some example code which has multiple types of Dice. The base type is a standard 6 sided Dice and it is using inheritance to make a 20 sided Dice.
123456789101112131415/
/
library code
struct Dice {
virtual ~Dice()
=
default;
/
/
Do a
"random"
roll of the dice
virtual
int
roll() {
return
6
; }
virtual
int
min
() {
return
1
; }
virtual
int
max
() {
return
6
; }
};
struct TwentySidedDice : public Dice {
virtual ~TwentySidedDice()
=
default;
/
/
Do multiple random rolls of the dice
virtual
int
roll(
int
times) {
return
times
*
3
; }
virtual
int
max
() {
return
20
; }
};
As with all good code, we have some unit tests to make sure everything is working:
1234567891011121314/
/
unit tests
void test_dice() {
Dice die;
assert
(die.roll()
=
=
6
);
assert
(die.
min
()
=
=
1
);
assert
(die.
max
()
=
=
6
);
}
void test_twenty_sided() {
TwentySidedDice die;
assert
(die.roll(
4
)
=
=
12
);
assert
(die.
min
()
=
=
1
);
assert
(die.
max
()
=
=
20
);
}
The above is compiled without any warnings on GCC (using -Wall
) and as all unit tests pass, we feel like we can ship the code as a library.
Problem with the code
If a client attempted to use the above code, they might try something like this:
123456789/
/
client code
int
main()
{
auto die
=
std::make_unique<TwentySidedDice>();
std::cout <<
"min is "
<< die
-
>
min
() << std::endl;
std::cout <<
"max is "
<< die
-
>
max
() << std::endl;
std::cout <<
"roll is "
<< die
-
>roll() << std::endl;
return
0
;
}
On first look this code seems reasonable and looks like it should work. However, you would see the following error:
123456789<source>: In function
'int main()'
:
<source>:
43
:
42
: error: no matching function
for
call to
'TwentySidedDice::roll()'
std::cout <<
"roll is "
<< die
-
>roll() << std::endl;
/
/
no matching function error
^
<source>:
16
:
17
: note: candidate:
'virtual int TwentySidedDice::roll(int)'
virtual
int
roll(
int
times) {
return
times
*
3
; }
^~~~
<source>:
16
:
17
: note: candidate expects
1
argument,
0
provided
Compiler returned:
1
The reason for the error is that based on the rules of C++ overload resolution the int roll(int i);
function in TwentySidedDice
is hiding the int roll();
function from Dice
.
Catching the error
Clang
If you compile the library code with clang (again with -Wall
) it gets the following warning:
12345678<source>:
16
:
17
: warning:
'TwentySidedDice::roll'
hides overloaded virtual function [
-
Woverloaded
-
virtual]
virtual
int
roll(
int
times) {
return
times
*
3
; }
^
<source>:
9
:
17
: note: hidden overloaded virtual function
'Dice::roll'
declared here: different number of parameters (
0
vs
1
)
virtual
int
roll() {
return
6
; }
^
1
warning generated.
Compiler returned:
0
This shows the error early and would let a developer know about the potential problem, so that it can be resolved before shipping to users.
GCC
As mentioned, on GCC with the compiler flags -Wall
, there are no errors in the code. To enable this warning on GCC you can explicitly add the -Woverloaded-virtual
flag.
MSVC
On MSVC compiling with the /W4
flag, doesn't show any warnings as the C4264
warning is off by default. To see the error you can enable warning C4264
or you can see the following using `/Wall:
1234<source>(
16
): warning C4263:
'int TwentySidedDice::roll(int)'
: member function does
not
override
any
base
class
virtual member function
<source>(
18
): warning C4264:
'int Dice::roll(void)'
: no override available
for
virtual member function
from
base
'Dice'
; function
is
hidden
<source>(
9
): note: see declaration of
'Dice::roll'
<source>(
7
): note: see declaration of
'Dice'
Comparing the errors
To view the errors from each compiler you can use godbolt to compare the output
No comments:
Post a Comment