1

I have abstract base class and an empty struct that should be used inside this class

//base.hpp
class Base {
public:
    virtual void do_something(const char* arr) {
        h = reinterpret_cast<Header*>(arr); //What happens here in the derived class? I would want it to cast it to the respective derived struct
        h->val = 10 //these are some common functionalities which I want to include in the base class
    }

private:
    Header *h;
}
//header.hpp
struct Header {
}

Both the class and struct are inherited

//derivedA.h
class DerivedA : public Base {
...
//this class should refer to HeaderA

}
//header.hpp
struct HeaderA : public Header{
    int val;
    bool is_it;
}

Similarly there's another derived class DerivedB and it's associated HeaderB. How can I make the derived classes refer to its respective derived structs?

One of the reasons I have an empty base struct even though they have few common members is that I am using reinterpret_cast. The common members in the incoming buffer are arranged differently. But it is useful to define common functionalities in the base class. What am I missing here? How can I make this work?

harsh
  • 905
  • 1
  • 10
  • 21
  • "Both the class and struct are inherited `class DerivedA : public Base `" - No they are not. This doesn't inherit from `Header`, only from `Base`. – Jesper Juhl Sep 03 '19 at 16:52
  • Please explain(with examples), exactly what You want to achieve. This abstract example is hard to understand for me. – Robert Andrzejuk Sep 03 '19 at 16:52
  • @JesperJuhl sorry, I edited my post. – harsh Sep 03 '19 at 16:56
  • @RobertAndrzejuk I am trying to come up with a better example. But basically I want the derived class to refer to the derived struct even though I am using the base struct in the base class for some common functionalities. There are more functionalities which are defined in the derived classes, if that makes sense – harsh Sep 03 '19 at 16:59
  • Would it make sense to make the class templated instead of having the Header as a private member? – harsh Sep 03 '19 at 17:14

1 Answers1

1

0) using reinterpret_cast on a char* is a Really Bad Idea. Change your base constructor to take a Header pointer. Or don't since they don't share anything, there's no real reason to to use inheritance here.

Maybe you're leaving out a bunch of stuff in the base Header for clarity's sake, I dunno. But as written, you don't need the base header pointer in the first place.

1) Make HeaderA inherit from Header.

2) DerivedA's constructor takes a HeaderA* parameter, which is then passed to the base class's Header*-based constructor.

3) DerivedA keeps a separate copy of what it was passed in, with the correct type.

So something like

class Base {
public:
  virtual ~Base() {} // virtual base destructor!  Very important.
  Base(Header* foo) 
    : header(foo) {
  }
private:
  Header* header;
...
};

// at the very least, you probably want a virtual destructor here too
struct Header {
  virtual ~Header() {}
};

struct HeaderA : public Header {
...
};

class DerivedA : public Base {
public:
  DerivedA(HeaderA* header) : 
    : Base(header)
    , headerA(header)
  {
    ...
  }
private:
  HeaderA* headerA;
};

Note that the ownership of HeaderA isn't addressed here At All. You might want to use a std::unique_ptr<Header> in the base class, and let RAII handle it for you everywhere else... rather than using references, like I have in the above code. Then at least there'd be some point to having it in the base class even if the header was completely empty.

Back to that cast from char* to Header*. I presume you're writing the header out with memcpy() or something like it either to a file, or a packet or some such, in an effort to be efficient.

Don't.

In both AAA MMORPG code bases I've worked on, each field of each struct was written out explicitly (to packets or files). It may have worked like an overly verbose memcpy() in some cases, but we made the effort, because the safety was Worth The Extra Effort. If you continue to do things the way you are, everything in your header has to be Right There.

  • no pointers
  • strings have to be fixed size arrays, arrays that will be mostly empty most of the time, because they're sized for your worst case scenario.
    • object pointers have to be replaced with object IDs, and that the object IDs have to be consistent between when they were written, and when they were read. And those IDs have to be stored in the struct, which means looking them up every time you have to use them.
  • You're locked into either "big endian" or "little endian" at write time, and heaven help you if your write "endian-ness" is different from your read "endian-ness". ("What the hell are big endian and little endian?" Google it. Right now. Seriously.)

By writing out fields individually you can do things like

  • write everything out in a particular endian-ness.
  • convert object pointers to object IDs.
  • write out a length and then the correct number of characters/bytes rather than a fixed/mostly-empty number of bytes.
  • encrypt field values (MMOs can't trust the data packets not to be sniffed so players can cheat)
  • change field order (it's fun to scramble your packets' field order and watch the hackers crash when they connect the first time, at which point you know which accounts to flag for Further Investigation. Yes, this is one of several reasons why your old version of some client won't work with the new version of the corresponding server.)
Mark Storer
  • 15,672
  • 3
  • 42
  • 80
  • Wow! Thanks for the detailed explanation. I think this might work. I'll get back to you. – harsh Sep 03 '19 at 17:18
  • One problem I see is that the Header is instantiated at run time, so passing a pointer in the constructor may not help I guess? Also I don't know if I can eliminate `reinterpret_cast` because how else would I "cast" a raw buffer to the respective HeaderX – harsh Sep 03 '19 at 17:33
  • 1
    You don't "cast" it at all. You read/write. Converting objects to bytes and back has a number of names. "Serialization", or "pickling/unpickling" are the two I can think of off the top of my head. There's a pretty good Q/A here on stackoverflow: https://stackoverflow.com/questions/523872/how-do-you-serialize-an-object-in-c. Solid "checked" answer, plus a number of other suggestions. – Mark Storer Sep 03 '19 at 18:29