views:

245

answers:

8

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!

A: 

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 );
}
Patrick
A: 

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 );
}
sbi
A: 

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"); }
};
SadSido
What's wrong with using `std::type_info`?
sbi
std::type_info is perfectly ok. But (I thought) it could be disabled by project settings...
SadSido
It can be disabled on some compilers. But that's rather stupid, disabling a standard C++ feature you're using.
MSalters
A: 

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>{};
Patrick
A: 

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);
AProgrammer
I'm fairly certain the latter would be undefined - by the ODR the constant initializer should have the same value in all translation units.
MSalters
@MSalters, the initializer in the later is no more a constant expression than in the former. And I've just reread 3.2 and specially 3.2/5 and see nothing which prevent the later to be well defined. (The reinterpret_cast will fail to compile on implementation where an unsigned isn't large enough to contains a pointer, but that is another problem).
AProgrammer
MSalters
+2  A: 

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.

MSalters
Okay, understanding check:When the compiler compiles a templated class, it builds a set of instructions (the various functions), a human-readable name, and a machine-readable name. Each of these things exist in memory, and so have a memory address, which is by definition unique per "instance" of the template, and the same for all identical "instances" of the template.std::type_info, when you inherit from it, allows access to this information?std::typeid(class c) is then the easy way to get a unique identifier?
Narfanator
Be careful with the "by definition unique" claim. For instance, may template functions are inline-able, and therefore multiple copies of those functions can exist, inlined in other functions. It's not really relevant here. The main point is that all types, including instances of class templates (but not templates themselves) have type_info available, and those have a compiler-provided order. This order is accessible through `type_info::before()`. The point of deriving a class from `type_info` is merely to adapt its interface for use as a key in a `std::map`
MSalters
+1  A: 

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. 
*/
pgast
A: 

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.

Narfanator