I am trying to create a general interface for derived classes using unique identifier numbers in C++.
This is what my code looks like (you need at least C++11 to compile it):
#include <iostream>
#include <memory>
class Base
{
protected:
int ident;
Base(int newIdent) : ident(newIdent) { }
public:
Base() : ident(0x01) { }
virtual ~Base() { } //needed to make class polymorphic
int getIdent() { return ident; }
};
class Derived : public Base
{
protected:
int answer;
Derived(int newIdent, int newAnswer) : Base(newIdent), answer(newAnswer) { }
public:
Derived(int newAnswer) : Base(0x11), answer(newAnswer) { }
int getAnswer() { return answer; }
};
int main()
{
std::shared_ptr<Base> bPtr = std::make_shared<Derived>(Derived(42));
std::cout << "ident = 0x" << std::hex << bPtr->getIdent() << std::dec << "\n";
if(bPtr->getIdent() & 0xF0 == 1)
{
std::shared_ptr<Derived> dPtr = std::dynamic_pointer_cast<Derived>(bPtr);
std::cout << "answer = " << dPtr->getAnswer() << "\n";
}
return 0;
}
Of course you should expect that the program outputs ident = 0x11
and answer = 42
, but it doesn't, because it exists normally after the ident = 0x11
line. I also did some examination with GDB and the disassembly of the critical if
condition check in the main function confirms the issue:
0x0000000000400f46 <+196>: call 0x401496 <std::__shared_ptr<Base, (__gnu_cxx::_Lock_policy)2>::operator->() const>
0x0000000000400f4b <+201>: mov rdi,rax
0x0000000000400f4e <+204>: call 0x4012bc <Base::getIdent()>
0x0000000000400f53 <+209>: mov eax,0x0
0x0000000000400f58 <+214>: test al,al
0x0000000000400f5a <+216>: je 0x400fb7 <main()+309>
When you break at *0x400f53
, rax nicely keeps the correct value (0x11
), but the following instruction just overwrites rax with zero, test
sets the zero flag and the je
instruction jumps to the end of the main function because the zero flag is set. What is going on here? Am I missing something or is the compiler (g++ 4.9.2
with x86_64-linux-gnu
target) generating wrong instructions?
Always compile with warnings enabled. Your problem is an order of operations issue where &
has lower precedence than ==
, so you need:
if ((bPtr->getIdent() & 0xF0) == 1)
Although then you're comparing against the wrong thing, so you really want:
if ((bPtr->getIdent() & 0xF0) == 0x10)
In this case, you would see this from gcc:
main.cpp:32:32: warning: suggest parentheses around comparison in operand of '&' [-Wparentheses]
if(bPtr->getIdent() & 0xF0 == 1)
~~~~~^~~~
or this from clang:
main.cpp:32:25: warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]
if(bPtr->getIdent() & 0xF0 == 1)
^~~~~~~~~~~
main.cpp:32:25: note: place parentheses around the '==' expression to silence this warning
if(bPtr->getIdent() & 0xF0 == 1)
^
( )
main.cpp:32:25: note: place parentheses around the & expression to evaluate it first
if(bPtr->getIdent() & 0xF0 == 1)
^
( )