3

I'm working on a basic client server application in C++ using sockets that will run a game of battleship. All communication between client and server is in the form of a simple object hierarchy that looks something like this:

namespace Message {
    enum MessageType { BASE, RESULT };

    class BattleshipMessage {
    public:

        virtual MessageType get_type() { return BASE; }
        virtual ~BattleshipMessage() {}
    };

    class ResultMessage : public BattleshipMessage {
    public:
        char _attackResult;

        ResultMessage( char result ) : _attackResult( result ) {}
        virtual MessageType get_type() { return RESULT; }

        ~ResultMessage() {}
    };
}

When receiving an object sent through the socket, I would like to be able to receive it as a generic BattleshipMessage and then based on the MessageType returned by get_type() cast it to the appropriate child class to retrieve whatever additional information I might need to process the message. I'm afraid that C# has made me soft as I've done this sort of thing with easy syntax like ResultMessage result = message as ResultMessage however I'm stuck trying to get my C++ implementation working.

BattleshipMessage* message;
recv( hAccepted, reinterpret_cast<char*>( &message ), sizeof(message), 0 );

switch ( message->get_type() ) {
case RESULT:
    if ( ResultMessage* result = dynamic_cast<ResultMessage*>(message)) {
        std::string celebration = "HOORAY!";
    }
    break;
}

I get Access violation reading location when I dereference the pointer and try to call get_type(). Can anyone point me in the right direction?

S Grimminck
  • 516
  • 4
  • 21
Jesse Carter
  • 20,062
  • 7
  • 64
  • 101
  • Shouldn't it be `reinterpret_cast( message )` since `message` is already a pointer? Otherwise you are changing the address `message` points to instead of the data pointed to by `message`. The answers have also raise some good points. – loganfsmyth Apr 21 '13 at 04:07

2 Answers2

3
sizeof(message)

gives the size of the pointer, which is typically 32-bit or 64-bit depending on your machine. You want

sizeof(BattleshipMessage)

which gives the size of the class. Even then, I'm not sure this is the right approach since each class object will contain a pointer to the vtable which handles the dynamic dispatch/virtual function calls, and sending the class across machines using the raw casting approach that you use will invalidate that pointer.

I think you should first serialize your object (i.e. convert it to a stream of characters) before sending it across the network, then deserialize to reconstruct the class: Is it possible to serialize and deserialize a class in C++?

Community
  • 1
  • 1
maditya
  • 8,626
  • 2
  • 28
  • 28
  • I tried changing the sizeof and I still get the same access violation message. I will look into the serialization techniques you posted. However, when dealing with a BattleshipMessage (as opposed to a pointer) the object is cast to a char* and sent across the network without an issue so I'm not sure if thats the problem? – Jesse Carter Apr 21 '13 at 02:25
  • 1
    You can't just cast the object because the pointer to the vtable is part of the class's memory footprint even though you can never access it directly. Casting it may compile and run if e.g. the memory address AFTER the space occupied by your class happens to contain the bits that constitute the '\0' character, but what you are sending is stil garbage. Serialization exists for this very reason. – maditya Apr 21 '13 at 02:29
  • Gonna mark this as the answer for the link to serialization. Haven't implemented the boost version yet but I've always relied on passing around serialized objects in the C# distributed apps I've played with. If I have more time I'll definitely apply that here cause I know its the right way to go for a more complex messaging system to work. – Jesse Carter Apr 21 '13 at 06:13
0

Get rid of all the virtual methods, and store MessageType as a member variable in BattleshipMethod. Then your code should work.

brian beuning
  • 2,836
  • 18
  • 22
  • Thats what I originally tried doing, but then I was unable to use dynamic_cast because BattleshipMessage wasn't considered to be a polymorphic class. I also get the same error message when trying to dereference the pointer to check the message type – Jesse Carter Apr 21 '13 at 02:35