1

Problem :

I have a set of Classes like a Database.I need to unpack certain response based on their opcodes.

eq : opcode : 01 Request : A

to unpack response from request A , I have created a class A. Likewise I have a no of Classes.

I need to unpack a byteArray into one of these classes based on the opcode.

Is there any mechanism in C++ to implement some sort of a map where I can lookup the opcode and choose the Class/Response to be unpacked

PS : Would love a readable Solution and some explanation too.

I have very little knowledge in JAVA but in JAVA i guess we have mechanisms using Object type, Class type etc.

Thanks

Marshel Abraham
  • 87
  • 2
  • 12
  • Ok, thanks for the info. I think I need to go back HighSchool :). But if you have got the essence of my problem Can you suggest a solution if any thanks – Marshel Abraham Nov 03 '14 at 20:02

3 Answers3

5

You can implement a factory: a map from opcodes (as integers/enums), class names (as strings), etc. to functions that instantiate classes dynamically:

enum Opcode {
    OpFoo,
    OpBar,
    OpQux,
};

// this should be a pure virtual ("abstract") base class
class Operation {
    // ...
};

class OperationFoo: public Operation {
    // this should be a non-abstract derived class
};

class OperationBar: public Operation {
    // this should be a non-abstract derived class too
};

std::unordered_map<Opcode, std::function<Operation *()>> factory {
    { OpFoo, []() { return new OperationFoo; } }
    { OpBar, []() { return new OperationBar; } }
    { OpQux, []() { return new OperationQux; } }
};

Opcode opc = ... // whatever
Operation *objectOfDynamicClass = factory[opc]();

As a bonus, you could (and probably should) use smart pointers (std::unique_ptr or std::shared_ptr) instead of raw pointers both in the return type of factory lambda functions and at other places you operate with abstract classes.

  • Because the `Opcode`s are simply consecutive unique IDs starting at 0, a `std::map` is probably more efficient. Actually, considering what they are, a simple raw array would be most efficient. – Deduplicator Nov 03 '14 at 20:06
  • @Deduplicator well then instead of building trees, we could as well store them in an `std::array` and avoid dynamic allocation altogether. I've opted to show this solution because it works in the general case, and I'm not sure efficiency is/should be a key point in a factory (dynamic allocation is already hopelessly slow compared to a hash table lookup). – The Paramagnetic Croissant Nov 03 '14 at 20:07
  • Note that `Operation` pretty much NEEDS a virtual destructor. – Mooing Duck Nov 03 '14 at 20:14
  • @MooingDuck yes, that's exactly right. For the sake of compactness, however, I didn't spell out the whole implementation of each and every class. I have provided the necessary starting point; I have even mentioned useful Google keywords like "pure abstract class" etc. Now it's OP's turn to do his research, implementation and debugging. – The Paramagnetic Croissant Nov 03 '14 at 20:16
  • hello thanks for ur help, i would like to know is ur solution aligned to any pattern in C++ ? Abstract Factory Builder Factory Method Object Pool Prototype Singleton – Marshel Abraham Nov 03 '14 at 20:39
  • @MarshelAbraham I did not design this code with any particular subtype of the factory pattern in mind. In fact, I dislike the factory pattern and in my opinion an object-oriented language should have sufficient reflection capabilities so that the factory pattern doesn't have to be used. – The Paramagnetic Croissant Nov 03 '14 at 20:41
  • hello, thanks a lot for ur effort, I am still trying to understand ur solution std::function : does this mean we are storing the constructor of the derived class as the function object ? – Marshel Abraham Nov 03 '14 at 23:15
  • yup i got it , returns a pointer to baseclass ...() denotes empty args thanks :) – Marshel Abraham Nov 03 '14 at 23:45
  • @MarshelAbraham No, it means we are storing a lambda function. It's not the constructor of the class. It's simply an anonymous function that returns a `new`'d object. – The Paramagnetic Croissant Nov 04 '14 at 06:10
  • thanks , I am using gcc 4.4.2. I assume there is no support for lambda's in that version, I am trying to figure out boost::lambda which is not quite sleek as this. Is there any way to sneek in C++ std:;lambdas and std::functions into my compiler -std=C++0x, options like these are failing...:( – Marshel Abraham Nov 04 '14 at 08:04
  • @MarshelAbraham GCC 4.4 is ancient. You should upgrade to at least 4.7 or preferably 4.8. However, without C++11 support, you can make a map from opcodes to factory **objects** (as opposed to lambdas). These are objects that have a virtual method which does the same thing as the lambdas: allocates objects. The only problem with this approach is that you will need to maintain two parallel hierarchies of classes (one being the original, and the other being the set of factory classes that mirrors it). – The Paramagnetic Croissant Nov 04 '14 at 08:46
3

You should understand that classes in C++ exist only at compile time. At runtime you only have vtables and possibly RTTI but the classes are forgotten. (This is different in Java, classes are existing at runtime and known to the JVM and the class loaders).

You probably want to use the factory pattern. For example, you might (in C++11) have a global map associating some names (or your opcodes) to factory functions building instances. this answer gives some sample code.

On some operating systems (or with some libraries like POCO) you might use dynamic loading & plugin facilities, like dlopen(3) & dlsym on Posix (e.g. Linux). Notice that dlsym gives access to an extern "C" function thru its (unmangled) name. See also this.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

A very templated solution, use like this:

class A {virtual ~A() = 0;};
inline A::~A() = default;

class B : A {};
class C : A {};
class D : B {};

// Define the factory
DEFINE_FACTORY(A, B, C, D)

// Want the factory?
factory<A> a_factory;
A* ap = a_factory(1, 0, 1, 2, 3); // Creates a C, passing 0,1,2,3 to the ctor
delete ap;
// Or directly?
make<std::unique_ptr>(1,0,1,2,3); // Creates a C, passing 0,1,2,3 to the ctor
// Immediately destroyed because the `std::unique_ptr` was not saved.

And here the template-code making it possible:

#include <utility>
#include <memory>
#include <exception>

template<class I, class... X> struct factory_base {
    template<class... ARGS> inline I* operator()(int i, ARGS&&... args) {
        return make(i, std::forward<ARGS>(args)...);
    }
    template<class... ARGS> using maker_t = I*(*)(ARGS...);
    template<class... ARGS> static I* make(int i, ARGS&&... args) {
        constexpr maker_t<ARGS...> maker[] = {make_helper<X, ARGS...>()...};
        if(i < sizeof...(X) && maker[i])
            return maker[i](std::forward<ARGS>(args)...);
        throw std::invalid_argument("The selected class cannot be constructed thus.");
    }
    template<class Y, class... ARGS> inline static constexpr maker_t<ARGS...>
    make_helper(...) {return nullptr;}
    template<class Y, class... ARGS> inline static constexpr auto make_helper()
    -> decltype(new Y(std::declval<ARGS>()...), maker_t<ARGS...>(0))
    {return static_cast<maker_t<ARGS...>>(
        [](ARGS&&... args){return new Y(std::forward<ARGS>(args)...);});}
};
template<class I> class factory;

#define DEFINE_FACTORY(A, ...) template<> struct factory<A> : \
    factory_base<A, __VA_ARG__> {}

template<class I, class... ARGS> std::unique_ptr<I> make(ARGS&&... args) {
    return factory<I>()(std::forward<ARGS>(args)...);
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118