1

I am sending a polymorphic object over serial between two Arduinos. The transmission should be fine, indeed if on the receiver side I static_cast the pointer to the Derived class it works properly. On the other hand if I try to call virtual functions on the Base pointer, Arduino resets. What is causing this behaviour?

Some samples of the code follows.

Sender:

RS485_Serial bus;
void loop ()
{
  Random r(1); //targeting device 1 in the bus
  if (r.request(bus)) { blink(r.val); } //make a request for a random number
                                        //blink the led the given number of times

  delay(1000);
}

Receiver:

RS485_Serial bus;
bus.set_address(1);
void loop ()
{
  listen(bus);
} 

Selected common parts:

struct Action {
  byte target_address;

  Action(byte addr): target_address(addr) {}
  virtual ~Action() {}

  virtual size_t size() const { return sizeof(*this); }
  virtual int  vrequest(RS485_Serial &) = 0;
  virtual void vrespond(RS485_Serial &) = 0;

  int request(RS485_Serial & bus) {
    bus.write(reinterpret_cast<byte*>(this), size());
    return vrequest(bus);
  }
  void respond(RS485_Serial & bus) {
    vrespond(bus); // <--- This virtual call makes the sender reset
  }
};

struct Random: Action {
  size_t size() const override { return sizeof(*this); }
  int val;
  Random(byte address): Action(address) {}

  int vrequest(RS485_Serial & bus) override {
    return bus.read(val);
  }
  void vrespond(RS485_Serial & bus) override {
    bus.write(static_cast<decltype(val)>(random(2,5)));
  }
};

void listen(RS485_Serial & bus) {
  byte * data = nullptr;
  byte length = 0;
  if ( bus.read(data, length) ){
    Action & a = *(reinterpret_cast<Action*>(data));
    if (a.size() == length) blink(3);
    if (a.target_address == bus.address) {
      a.respond(bus);
      //static_cast<Random*>(&a)->vrespond(); works but it has no point
    }
  }
}

My doubt is that if the vtable is stored in different positions, than the vtable pointer in the object obtained by the receiver will be off, causing the crash.

13
  • 2
    I'm voting to close this question as off-topic because this is general C++ programming, not specific to Arduino (other that the incidental use of a serial link). – user31481 Aug 5 '17 at 12:12
  • 1
    Please edit your question to include complete, minimal sketches that demonstrate the issue. – per1234 Aug 5 '17 at 12:13
  • @LookAlterno could this be moved to stackexchange? – DarioP Aug 5 '17 at 12:15
  • 1
    @LookAlterno - I agree and disagree, because there is some element of hardware (serial) that could be causing the issues, most likely because the data is being sent as binary and read as text or similar. – Code Gorilla Aug 5 '17 at 13:51
  • 1
    I think you're going to have to show your code for both sender and receiver for us to have any clue what is or might be going on. – Majenko Aug 5 '17 at 14:53
1

I think http://stackoverflow.com is the correct forum for your question. Your problem is how to serialize/deserialize correctly an object in C++.

An easy way to do it is simply send the values (not the object) and recreate the object at the other side. So, now your problem is reduced to send and receive a bunch of int's, float's, string's, whatever.

Why you want/need to do that?

4
  • The object is serialised fine, but I think that there might be inconsistencies between the vtables allocated on the two boards. The aim is to have an arduino launching functions on another one which I planned to do encapsulating lambdas inside polymorphic objects. I may just fall back to a plain old switch statement. – DarioP Aug 5 '17 at 12:29
  • 2
    I looks like you want to send some code (as opposed some data) to be executed at the other Arduino. Arduino uses Harvard architecture, so you can't put code in RAM and execute it. I'm curious about what are you trying to do. – user31481 Aug 5 '17 at 12:36
  • Also take in mind sometimes on different CPUs int values from 2 to 4 bytes can be stored differently (it's called little/big endian). – Michel Keijzers Aug 5 '17 at 17:02
  • @DarioP the point is that you can't serialize the methods of an object between machines of this type; you can only serialize the data which uniquely specifies of object of the class. You need to have a compatible class on the receiving side, and inflate an instance of it with a constructor taking the serialized data. – Chris Stratton Aug 6 '17 at 20:54
1

I suspect then that the object contains a pointer to the code for vrespond, which is at a specific (and differing from program to program) location in flash memory. An object in C++ isn't a single atomic item, it is a collection of data and pointers to functions. On a Harvard architecture system the data is in RAM and the code is in Flash (you cannot typically execute code from RAM). The compiler chooses where to place the code in Flash when it compiles (well, the linker does). That won't be the same address in different programs. All you are doing is sending over the addresses in flash that the code resides at - and those addresses are incorrect. By specifically casting it to a type that has code available you are also telling it where that code resides. Without that manual cast you are having to rely on the (incorrect) function pointers.

It is a bad idea to transfer objects like that from one system to another. It is better to transfer just the data and feed that data into an existing object created on the receiver. How you send and receive that data and feed it into the object is up to you.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.