I am designing a DNS parsing library in C++. A DNS Packet has a set of standard fields followed by a list of Resource Records that again has a set of standard fields followed by a RData field. The RData field is parsed based on the type field. Now, I specify a hierarchy for DNSRData, to handle various types. The code looks like something like this:
class DNSRData {
virtual void ToString() = 0;
virtual void Parse() = 0;
}
class DNSRData_A : public DNSRData {
void ToString();
void Parse();
uint32_t GetIP();
}
class DNSRData_CNAME : public DNSRData {
void ToString();
void Parse();
const char* GetAlias();
}
class DNSResourceRecord {
/* Standard Fields
..... */
int type_; // Specifies the format for rdata_
DNSRData *rdata_;
}
class DNSPacket {
/* Standard Fields
....*/
vector<DNSResourceRecord *> rr_list_;
}
Now this is the issue I have, each DNSRData record might have different fields. I do not want to add accessors for all the fields in the Base class, as they are present in some derived class and not in others e.g. IP address is present only in DNSRData_A and not in any other.
So, when I want to perform any operations on the DNSRData, I lookup the type and perform a downcast from DNSRData* to DNSRData_A*.
DNSRData *rdata = packet->GetResourceRecord().front(); //not really necessary for this example
if(resource_record.type == RR_CNAME) {
DNSRData_CNAME *cname = (DNSRData_CNAME*)rdata;
}
This can cause tons of issues later on, and as we add more types it is quickly becoming an unholy mess. Any ideas on how to solve this problem without adding all accessors to the Base class ?
EDIT:
Some more context, this is part of a high performance DNS trace parse library. A lot of the operations are done as we see packets on the wire. So, what would be an operation that messes up the design, lets say we get a DNSPacket and we parse it now we want to decide how to process it further based on the type.
if(type == RR_CNAME) {
DNSRData_CNAME *cname = dynamic_cast<DNSRData_CNAME*>(&rdata);
char *alias = cname->GetAlias();
}else if (type = RR_A) {
DNSRData_A *a = dynamic_cast<DNSRData_A*>(&rdata);
uint32_t ip = a->GetIP();
}
As you see there is a downcast involved, from the base type RData to a more specific RData type. I want to avoid this downcast and use maybe a design pattern to solve this problem.
if i understand you correctly i think that the Visitor pattern is what you are looking for
Visitor pattern.