Home > Back-end >  Concept to keep track of class instantiations in C
Concept to keep track of class instantiations in C

Time:11-12

I am trying to write a code that keeps track of instances of my class. Each instance is uniquely identified by a type (int). I would like to have some kind a map which links a type to a instantiation of my class.

My idea was to use a static map for this, and every instantiation registers itself when the constructor is called:

#include <unordered_map>
#include <iostream>

class Dummy {
    public:
    Dummy(double a, double b, double c, int type);
   
    void GetContents() {std::cout << type_ <<": a: " << a_ << " b: " << b_ << ", c: " << c_ << std::endl;}
    private:
        const double a_;
        const double b_;
        const double c_;
        const int type_;
};

static std::unordered_map<int, Dummy> Type_Map{{12, Dummy(1, 2, 3, 12)}}; // pre-defined instantiation

Dummy::Dummy(double a, double b, double c, int type) : a_(a), b_(b), c_(c), type_(type) {
        Type_Map.insert({type, *this});
    };

In this case, everything seems to work well:

int main()
{
    Dummy(12, 6, 23, 3);
    Dummy(8, 22, -4, 7);
    for (auto a : Type_Map) {
        std::cout << a.first << ", ";
        a.second.GetContents();
    }

}

which gives a correct output:

3, 3: a: 12 b: 6, c: 23
7, 7: a: 8 b: 22, c: -4
12, 12: a: 1 b: 2, c: 3

However, outside of this minimal example and in my real-life project, this seems to be unstable. While the instantiation is still registered in the map inside of the constructor, the map entry is lost as soon as I leave the constructor.

I believe this may have to do with the *this call inside the constructor where the object is not yet correctly initialized? Is this ill-defined behavior?

Is there a better concept that achieves what I want to do?

PS: What I actually need

The code is written this way because I have some instantiations of my StaticData class and many instantiations of my DynamicData class. The content of StaticData instantiations do not change (all members are const), and there are only a few instantiations (this corresponds to the Dummy class in my minimal example).

However, there are a lot of DynamicData instantiations. For every DynamicData instantiation, there is a specific StaticData object which belongs to it. I want to avoid that every DynamicData object has to carry a StaticData object. Therefore, it only carries an id that links it uniquely to a StaticData object. If the user wants to find out which StaticData belongs to my specific DynamicData object, we can just look in the map (this will not happen regularly during runtime, but we still need to keep the information).

The obvious solution would be to just use pointers. However, I want the user to be able to just initialize a DynamicData object with the id instead of having to pass a pointer to a StaticData object.

CodePudding user response:

What you want is a registry, a specific object where you create all of the StaticData objects you will ever intend to use (along with their associated IDs). If someone needs a particular StaticData configuration, they add it to the registry. Other code can reference objects in the registry by ID.

The important point is that they do not reference them by constructing an object. They never make a StaticData; they ask the registry for a pointer/reference to one which already exists. The lifetime of all such objects is controlled and managed in the registry, and nobody else gets to manage such things.

You're trying to make the registry implicit by hiding it behind StaticData's constructor. That is fertile ground for creating lifetime issues, as the ownership relationship between the StaticData you create and the one stored in the registry is... murky. It's best to make it explicit by directly asking the registry for a reference to the object. It makes it clear that you get to access the object, but you don't own it.

CodePudding user response:

Your problem is that the static map is filled inside the constructor, but outside of the constructor, the entry is gone. It seems like there are two different instances of the static map. You see one instance inside the constructor, and another instance outside of the constructor.

It looks like, you define your static map in a header file. Each .cpp file that includes the header file will "see" its own instance of the static map. Solution: Make a class which keeps the map as a static member. If this does not solve your problem, make also sure that you define the static member inside a .cpp file, like:

map<int, Dummy> TypeRegistry::Type_Map;

  • Related