Juraj Blaho
Juraj Blaho

Reputation: 13451

Address of a static variable

I am trying to do a simple class to unique ID conversion. I am thinking about adding a static method:

class A {
  static int const *GetId() {
    static int const id;
    return &id;
  }
};

Each class would then be identified by unique int const *. Is this guaranteed to work? Will the returned pointer really be unique? Is there any better simpler solution?

I have also thought about pointer to std::type_info:

class A {
  static std::type_info const *GetId() {
    return &typeid(A);
  }
};

Is that better?

Edit:

I don't need to use the id for serialization. I only want to identify a small set of base classes and I want all subclasses of some class to have the same id

Upvotes: 15

Views: 8335

Answers (9)

Juraj Blaho
Juraj Blaho

Reputation: 13451

As I have noticed at least MSVC 2008 or 2010 optimizes the static variable, so that the following GetId function returns same address even for different classes.

static int const *GetId() {
    static const int i = 0;
    return &i;
}

Therefore address of uninitialized constant static variable may not be used for identification. The simplest fix is to just remove const:

static int *GetId() {
    static int i;
    return &i;
}

Another solution to generate IDs, that seems to work, is to use a global function as a counter:

int Counter() {
    static int i = 0;
    return i++;
}

And then define the following method in the classes to be identified:

static int GetId() {
    static const int i = Counter();
    return i;
}

As the method to be defined is always the same, it may be put to a base class:

template<typename Derived>
struct Identified {
    static int GetId() {
        static const int i = Counter();
        return i;
    }
};

And then use a curiously recurring pattern:

class A: public Identified<A> {
    // ...
};

Upvotes: 7

eyesathousand
eyesathousand

Reputation: 587

Because you will have to add this method to all classes that require a UID you may as well do this.

unsigned int getUID()
{
    return 12;
}

The advantage of this is that the compiler will be able to use a jump table if you are using this for RTTI for switching on type, which will not be possible with two pointers because the jump table would be very sparse.

The (minor) disadvantage is you need to keep track of which identifiers have been taken.

A major disadvantage of the first method you presented is that the same numbers cannot be used to identify objects because the virtual getUID() method won't be able to take the address of the variable in another function's scope.

Upvotes: -2

James Kanze
James Kanze

Reputation: 153899

The address of the static int is guaranteed to be unique for each function (and the same for every call to the same function). As such, it can work very well as an id within a single execution of the code. The address could conceivably change from one run to the next, and will often change from one compilation to the next (if you've changed anything in the code), so it is not a good solution for an external id. (You don't say whether the id must be valid outside a single execution or not.)

The address of the results of a typeid is not guaranteed to be the same each time you call the function (although it probably will be). You could use it to initialize a pointer, however:

static std::type_info const& GetId()
{
    static std::type_info const* id = &typeid(A);
    return id;
}

Compared to using int*, this has the advantage of providing additional information (e.g. for debugging). Like int*, the identifier may be different from one run to the next; A::GetId()->name() will point to the same '\0' terminated string (although again the address might be different) provided you compile with the same compiler. (As far as I can tell, the standard doesn't guarantee this, but in practice, I think you're safe.) Change compilers, however, and all bets are off.

The solution I've used in the past is something like:

static char const* GetId()
{
    return "A";  //  Or whatever the name of the class is.
}

This provides a unique identifier, easily compared, within a single execution of the code, and a string value which can be used as an external identifier, and which is guaranteed across all compilers. We implemented this as a macro, which defined both the static function, and a virtual function which returned it, e.g.:

#define DECLARE_IDENTIFIER(name)                                    \
    static char const* classId() { return STRINGIZE(name); }        \
    virtual char const* id() { return classId(); }

This results in a very fast (but limited) RTTI, and supports external identifiers for serialization and persistency.

Upvotes: 2

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145204

The address of static variable is guaranteed to be unique and the same in all translation units.

It is not a good idea because it requires you to add code to every class to be identified.

The pointers to type info objects are not guaranteed to be unique, but the type info objects themselves are guaranteed to compare as equal for a given class, and as unequal for distinct classes. This means you can use small wrapper objects that carry type info pointers, and the delegate the comparisions to the type info objects. C++11 has such a wrapper in the standard library, and if you don't have access to that, there is one in Andrei Alexandrescu's "Modern C++ Design", and therefore probably also in the Loki library, there is probably one in Boost, and there is one on my Wordpress blog – it's not like you to have invent one from scratch.

If, however, the id's are to be used for serialization, then you need id's that are valid across builds. And in that case you you need strings or UUIDs. I would go with UUIDs.

To associate a class with an UUID you can in general use a type traits class. Or if you're only doing Windows programming then you can use Visual C++'s language extensions for this. I think but I am not 100% sure that those language extensions are also implemented by g++ (in Windows).

Cheers & hth.

Upvotes: 5

Valmond
Valmond

Reputation: 2959

Static variables are initialized before Heap & Stack memory so yeah it will be unique.

Quirky though.

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612794

Clearly pointers to different variables must have different values. Just watch out if you choose to derive a subclass of A. You need to decide what your policy is for id. If you did nothing then the subclass would have the same id.

Upvotes: 1

Ayjay
Ayjay

Reputation: 3433

In general, you really really want to avoid hacky things like this. If I really had to do this, I'd look at using some UUID system (there's a library in Boost for that, but I'm not very familiar with it), or some singleton that maintained a list of these objects for whatever purpose you need.

Upvotes: 0

thiton
thiton

Reputation: 36049

The int* method would be unique, since a different static memory cell must be allocated for each static variable, and I'd guess it is simpler to understandthan the type_info idea.

Upvotes: 1

sharptooth
sharptooth

Reputation: 170479

Yes, this will work. Each static local will be given distinct memory location at the time when the module is loaded and it will persist until the module is unloaded. Remember, static locals are stored in static storage that is distributed during compilation and they persist till the module gets unloaded, so they will have distinct memory locations.

Upvotes: 9

Related Questions