0

I have a class in c++ named InformationElement which defines the following Information Element frame structure:

<-Element ID-> <-Element Length- ><-Variable Payload->

  1. The element ID is 1 Byte.
  2. The Element Length is 1 Byte Long. It defines the length of the payload part.
  3. The class contains virtual functions used for serialization and deserialziation of the contents + defining the type of the ElementID.

From this class different derived classes are inherited for example:

  1. class Capabilities
  2. class Operation.
  3. class TimingParameters.

Each of the derived class has a unique Element ID and different payload.

Different Information Elements (IEs) will be encapsulated in a bigger frame. This bigger frame contains the number of Information Elements that are encapsulated in it.

<-Number of IEs-> <--IE 1--> <-- IE 2 --> <-- IE 3 --> ......

From the transmitter point of view there is no problem in serializing these information. However, in the receiver side, the receiver has to extract the Element ID and based on that value the receiver chooses the correct derived class to handle the payload part i.e. handle the deserialization operation. The traditional way of doing this in the receiver side is to build a big switch case as following:

InformationElement *element;
switch (elementID)
{
  case 1:
    element = new Capabilties;
  case 2:
    element = new Operation;
  case 3:
    element = new TimingParameters;
}

However, if I have 100 elements, it would be too much to compare with and it won't scale that much.

So my question is there any smart way to do this in c++ better than inserting a unique case for each independent Element ID?

  • 1
    What's wrong with a switch statement? I'd prefer that to any other solution I think. – Barry Aug 14 '15 at 14:33
  • You'll need to express the association between each identifier and the corresponding type *somewhere*. Would a solution needing a bit of the association inside each type fit your bill ? – Quentin Aug 14 '15 at 14:42
  • @Quentin, so what do you propose? –  Aug 14 '15 at 14:44
  • Abusing static object constructors to associate ID's and factory functions, such as `RegisterType dummy(3, constructor);`. You'd typically put one inside each of the classes' cpp files. – Quentin Aug 14 '15 at 14:54

2 Answers2

1

Take the switch-case and replace it with something equivalent. For example,

typedef InformationElement * (*ElementCreatorFunction)();
std::map< ElementEnum, ElementCreatorFunction > ElementCreatorMap {
    { 1, []() -> InformationElement * { return new Capabilities; } },
    { 2, []() -> InformationElement * { return new Operation; } },
    { 3, []() -> InformationElement * { return new TimingParameters; } }
};

InformationElement * element = ElementCreatorMap[ elementID ] ();

This allows for runtime customization and modularity. The bodies of the "cases" can be in different source files or in dynamically loaded modules.

A smaller change would be to replace the error-prone switch-case statement with something similar but safer. My safe_switch library provides CASE without needing break;, which has been forgotten in your example.

By the way, it looks like std::unique_ptr< InformationElement > might be more appropriate than InformationElement *.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • in your solution I returned to the same problem again of having to add each new information element. So why did you propose this solution? Is there any different in terms of comparison speed and selecting the correct derived class –  Aug 14 '15 at 14:38
  • 1
    This looks like a very very fancy `switch` to me. – Quentin Aug 14 '15 at 14:39
  • 1
    @IoT You mentioned scalability. This solution is easier to extend — see edit. As for performance, the `switch` statement is probably unbeatable, but you didn't mention performance in the question. – Potatoswatter Aug 14 '15 at 14:42
  • If `ElementEnum` were a plain integral type, you could easily implement a system by which new implementations of `InformationElement` can register themselves to this "factory", without the factory having any dependency on them. – juanchopanza Aug 14 '15 at 15:15
1

I suggest using a factory. See the Boost.Functional/Factory for a reasonable implementation.

If you do not want to use boost, Factory, from wikibooks, shows a simplified C++ implementation that may help get you started.

A big omission from the wikibooks discussion is lack of semi-automated registration, which is discussed on this blog post Factory Design Pattern in C++ and in two articles on CodeProject A C++ Object Factory and Factory Pattern in C++.

Of course, there is a SO answer to this too: Is there a way to instantiate objects from a string holding their class name?

Community
  • 1
  • 1
Phil
  • 5,822
  • 2
  • 31
  • 60
  • I don't want to use any external libraries. Is there any possibility to do that without using Boost? –  Aug 14 '15 at 14:46
  • 1
    This is a non-answer. You need to determine the type to get a factory object in the first place. The lambdas in my snippet implement the factory pattern, they just don't make it look like a big deal. The linked Q&A begs the question, by putting the switch inside a class. – Potatoswatter Aug 14 '15 at 15:51