1

I need to be able to retrieve a type based on an ID.

To achieve this, I am intending to create a base class for the types, which declares all the functions as pure virtual functions.

class Base
{
public:
    virtual int function1() = 0;
    virtual int function2() = 0;
};

I then create derived classes, instantiate these and store the pointers to the instances in an std::unordered_map of base pointers.

std::unordered_map<int, Base*> types;

Each element of the std::unordered_map will point to a different derived class type (i.e. no duplicates).

Then I can retrieve the type I need by getting the associated member from the std::unordered_map.

Base* ptr = types[4];

// Use type...
int num = ptr->function1();

Now I know this could come down to opinion, but is this a horrible use of C++? Is there a cleaner way to do this?

Update

I could use instance of the derived classes instead of their respective IDs, however I would likely have to dynamically allocate each one on the heap (i.e. using new), and I am going to be creating a lot of these, so the overhead from dynamic allocation could have an unwanted effect on performance.

brettwhiteman
  • 4,210
  • 2
  • 29
  • 38
  • Erm, if you already know the index where the *type* would be, why not use a `std::tuple<>` and then call `std::get<>` to give you the instance at the correct index. Then you don't need some horrible inheritance hierarchy enforced on unrelated things.. – Nim Sep 01 '14 at 09:29
  • 1
    Your `Base` class seems to be a perfectly standard example of a class meant to be used polymorphically (but add a virtual destructor), and the map is in essence a factory that is perhaps too light on the abstraction (it leaks a lot of information to the user, perhaps that's too much). But on principle it seems OK. – Jon Sep 01 '14 at 09:37
  • @Nim there are going to be a lot of types involved here. I'd rather not have things like std::tuple sitting around in my code. And not to mention if something goes wrong... the errors generated by the compiler... I'm having nightmares already. – brettwhiteman Sep 01 '14 at 09:40
  • what are you going to do with those "types"? what you have there is just an array/map of pointers to instances and not types (as in meta-pragramming). @Jon's suggestion to use factory pattern would help create arbitrary instance. – mariusm Sep 01 '14 at 09:49
  • @MariusM yes but the instances are of different types, and I can use the functions that the type exposes. That's what I want to do it for, not to get a particular instance of a type, but rather to use a particular type. – brettwhiteman Sep 01 '14 at 09:52
  • Then you have a registry, i.e. you will have to register/add the instances before you lookup. You should use unordered_map::find to retrieve and check that it is not unoredered_map::end() (i.e. that it exists). – mariusm Sep 01 '14 at 10:02
  • Use some smart pointer in your map, not a raw pointer. – Basile Starynkevitch Sep 01 '14 at 10:19
  • @BasileStarynkevitch good point, thanks. – brettwhiteman Sep 01 '14 at 10:47

2 Answers2

2

Now I know this could come down to opinion, but is this a horrible use of C++? Is there a cleaner way to do this?

I can see only one thing in the example code that could conceivably be called horrible, and that's the lack of abstraction. However, the concept itself is perfectly OK.

There are two pieces of machinery here: a polymorphic base class, and a map that gives you an object instance based on some kind of opaque id -- an int in the example, but it could also be e.g. an std::string and nothing would really change.

The base class is as classic as it gets, with the provision that it should also have a virtual destructor as per standard good practice. Apart from that there's nothing to see there.

The map is conceptually what you could call a "singleton factory". It's a factory because you call a method passing an id and you get back an object that corresponds to that id, with the only distinction that you get the same instance back every time you call the method with the same id. From a high-level perspective, this is not at all interesting to the client. The client just cares that e.g. every time they pass in 4, they get back a Widget*. How the factory gets hold of that instance in order to return the pointer is simply not important when you are looking at client code. So there's nothing out of the ordinary here also, at least on the design level.

Extending this line of thought leads to the possible deficiencies: there is no need for the client to know how the factory satisfies requests, but exposing the implementation (direct access to std::unordered_map) lets the client know everything, and it also lets them do things with the factory that they should perhaps not be allowed to do (e.g. unregistering types). This can be easily fixed by creating a new class that encapsulates the map and exposes a public interface that you have free reign to design as you see fit.

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
-1

is this horrible use of C++? -- Yes, perhaps better expression is "no use of polymorphism", i.e. you are not using polymorphism (properly).

You don't need to track types in the first place as compiler does it for you through polymorphism:

1) just declare the method virtual in the base class (you already did that).

2) overwrite the method in child-classes.

like:

class Base {
public:
   virtual int myId(){ return 0; }
};
class Child1: public Base {
public:
   int myId() { return 1; }
};
mariusm
  • 1,483
  • 1
  • 11
  • 26
  • That does not answer my question. Say I have an ID of 31, how do I get the corresponding type? – brettwhiteman Sep 01 '14 at 09:35
  • 1
    He's not trying to track types. He is simply using a factory. – Jon Sep 01 '14 at 09:39
  • @Jon that would make sense, but he is trying to track types. – mariusm Sep 01 '14 at 09:44
  • 1
    @MariusM: No, he's not. He doesn't want to find out what type an object is given a pointer to it (which is how I interpret "tracking types"); he wants to get an object given an opaque id for it. – Jon Sep 01 '14 at 09:46
  • @Jon I was just wondering how to answer MariusM, and saw your comment appear, and you nailed it! I think you know what I'm trying to do better than I do myself! Why don't you submit an answer explaining the factory design pattern and how I can improve my implementation of it? – brettwhiteman Sep 01 '14 at 09:49
  • factory pattern, see create by value? http://stackoverflow.com/questions/5120768/how-to-implement-the-factory-pattern-in-c-correctly – mariusm Sep 01 '14 at 09:53
  • 1
    @MariusM: That's not the [factory](http://www.oodesign.com/factory-pattern.html) pattern, it's *[factory method](http://www.oodesign.com/factory-method-pattern.html)*. Huge difference. – Jon Sep 01 '14 at 09:56
  • @Brett: I added an answer, which I 'll try to enrich with some links shortly. – Jon Sep 01 '14 at 10:13