Question

Core problem: I want to be able to take an instance of a templated class, say:

template<class a, class b, class c> class foo;

foo<int, float, double>;

and then do something like:

foo<int, float, double>::value;  //Evaluates to a unique number
foo<long, float, double>::value; //Evaluates to a different unique number
foo<int, float, double>::value;  //Evaulates to the same unique number

Except, really, it's:

template<class a, class b, class c>
int getUniqueIdentifier()
{
    return foo<a, b, c>::value;
} 

Current Solution Attempt:
I'm thinking I want to use Boost::MPL's "Extensible Associative Sequence", since each element gets it's own unique identifier, but I think I need to be able to alter the sequence in place, which "insert" doesn't do.
I may be barking up the wrong tree. (On the plus side, dayum, but MPL!)

Purpose:
Reinventing the wheel on a Signals & Sockets system. Components make and register channels with a "switchboard", which would use the unique identifiers to put the channels in a map, allowing run-time versatility. I've tried looking up the Qt library as an example, but I can't parse their abbreviations, and I think I'm missing some formal know-how.

Thanks!

Was it helpful?

Solution

If you want to put things in a map, and need a per-type key, the proper solution is to use std::type_info::before(). It may be worthwhile to derive a class so you can provide operator<, alternatively wrap std::type_info::before() in a binary predicate.

OTHER TIPS

I just happened to have this piece of hackery lying around in my libs(the uintxx are my typedefs with obvious meanings) - works without rtti. 32/64 compat. first few template are to define a pointer_uint that contains a void*

namespace pgast{

template <size_t size>
struct _pointer_uint{
};

template <>
struct _pointer_uint<2>{
    typedef uint16 intrinsic;
};

template <>
struct _pointer_uint<3>{
    typedef uint32 intrinsic;
};

template <>
struct _pointer_uint<4>{
    typedef uint32 intrinsic;
};

template <>
struct _pointer_uint<5>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<6>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<7>{
    typedef uint64 intrinsic;
};

template <>
struct _pointer_uint<8>{
    typedef uint64 intrinsic;
};

typedef _pointer_uint< sizeof(void*) >::intrinsic pointer_uint; 

template <class c>
struct Class_Identifier{
   static pointer_uint id(){
       static char _id;
       return reinterpret_cast<pointer_uint>(&_id);
   }
   template <class c2> 
   bool operator==(const Class_Identifier<c2>& rhs)const{
      return id() == Class_Identifier<c2>::id();
   }
   template <class c2> 
   bool operator<(const Class_Identifier<c2>& rhs)const{
      return id() < Class_Identifier<c2>::id();
   }
};

}//namespace pgast

/*
Copyright (c)1993,2001 J. E. Pendergast Jr. 
*/

There's a good chance of collisions but for simplicity you can't beat:

template<class a, class b, class c>
static int getUniqueIdentifier()
{
    return sizeof( a ) + sizeof( b ) + sizeof( c );
}

If you have an rtti enabled, you can concat the names of the classes, your template is being instantiated with, and then return a hash for this string. By choosing a good hash function, you minimize the chance of collision (and maybe you don't need an rtti as well, you can introduce some rtti of your own):

// basic template for gaining names of the classes:
template<typename T>
class Rtti
{
static std::string GetRTTIName() { return std::string(); }
};

// specialization for the classes, you expect to use:
template<>
class Rtti<float>
{
static std::string GetRTTIName() { return std::string("float"); }
};

If you're doing this all in one header file, you could use the __LINE__ macro to get some unique number. However, as soon as this is spread out over more than one file, they aren't unique anymore.

IIRC, VC has some macro that evaluates to a number which is incremented each time the macro is used. So if you're on VC only, you can probably use this. However, this, too, will only work within one translation unit.

Other than that, I don't have any idea how to get unique numbers out of types.

However, why don't you use a typeid as the map's key type? Something like this:

typedef std::map< std::type_info, whatever > my_map;

template< typename a, typename b, typename c>
void register_foo(my_map& the_map, const foo<a,b,c>& some_bar, whatever the_whatever) 
{
  the_map.insert( typeid(foo<a,b,c>), the_whatever );
}

Or how about:

template<class a, class b, class c, int uniqueId>
class Test
{
public:
    int uid() {
        return uniqueId;
    }
};

int main(int argc, char* argv[])
{
    Test<int, int, int, 5> test1;

    std::cout << test1.uid() << std::endl;

    return 0;
}

If you want all instances with the same template parameters to have the same id you could use;

template<class a, class b, class c>
class TestId5 : public Test<a, b, c, 5>{};

Do you want it per instance or per instantiation? For the later

template <...>
class foo 
{
public:
   static unsigned id;
};

extern unsigned fooCounter;

template <...>
unsigned foo::id = ++fooCounter;

or even

template <...>
unsigned foo::id = reinterpret_cast<unsigned>(&id);

Revisiting this problem; it seems to me that if I can guarantee that some function in the instantiation of the template is unique to that instantiation, I can simply use the address of that function as the ID.

I though of this when I realized I didn't have to use the actual string from type_id, I could just use the location of the string.

So, for instance;

template<class a>
foo
{
    void id(){}
    ...
}

would

&foo<int>::id != &foo<float>::id  //?

If that's true, then I can use that as my unique-per-specialization ID number for the map, and not rely on RTTI.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top