Уникальный идентификатор типа класса, который является безопасным и поддерживает границы библиотек - PullRequest
9 голосов
/ 28 мая 2009

Буду признателен за любую помощь, поскольку C ++ не является моим основным языком.

У меня есть шаблонный класс, полученный из нескольких библиотек. Я пытаюсь найти способ уникально назначить идентификатор int для каждого производного класса. Я должен быть в состоянии сделать это из статического метода, то есть.

<code>
template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
    }
    // ...
};
Спасибо!

Ответы [ 10 ]

11 голосов
/ 07 октября 2011

Это можно сделать с очень небольшим кодом:

template < class DERIVED >
class Foo
{
public:
    static int s_id()
    {
        return reinterpret_cast<int>(&s_id);
    }
};
8 голосов
/ 29 мая 2009

В современном C ++ (03 - при условии, что вы используете недавний компилятор, такой как gcc), вы можете использовать ключевое слово typeid , чтобы получить объект type_info, который предоставляет базовую информацию о типах по крайней мере во время выполнения - это стандартная (а затем кроссплатформенная) функция .

Я взял пример из википедии и добавил проверку шаблона / наследования, похоже, она работает хорошо, но я не уверен в версии int (это взлом, основанный на предположении, что компилятор будет иметь имена типов где-то в только для чтения памяти ... это может быть неверное предположение).

Строковый идентификатор кажется гораздо лучше для кроссплатформенной идентификации, если вы можете использовать его в вашем случае. Он не совместим с кросс-компилятором, так как имя, которое он вам дает, является «определенной реализацией» стандартом - как это предлагается в комментариях.

Полный код приложения теста:

#include <iostream>
#include <typeinfo>  //for 'typeid' to work

class Person 
{
public:
   // ... Person members ...
   virtual ~Person() {}
};

class Employee : public Person 
{
   // ... Employee members ...
};

template< typename DERIVED >
class Test
{
public:
    static int s_id()
    {
        // return id unique for DERIVED
        // NOT SURE IT WILL BE REALLY UNIQUE FOR EACH CLASS!!
        static const int id = reinterpret_cast<int>(typeid( DERIVED ).name());
        return id;
    }

    static const char* s_name()
    {
        // return id unique for DERIVED
        // ALWAYS VALID BUT STRING, NOT INT - BUT VALID AND CROSS-PLATFORM/CROSS-VERSION COMPATBLE
        // AS FAR AS YOU KEEP THE CLASS NAME
        return typeid( DERIVED ).name();
    }
};

int wmain () 
{
    Person person;
    Employee employee;
    Person *ptr = &employee;



    std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)
    std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
    std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)
    std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time
                                                    // because it is the dereference of a pointer to a polymorphic class)

    Test<int> test;
    std::cout << typeid(test).name() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_id() << std::endl;    
    std::cout << test.s_name() << std::endl;    

    Test< Person > test_person;
    std::cout << test_person.s_name() << std::endl;    
    std::cout << test_person.s_id() << std::endl;    

    Test< Employee > test_employee;
    std::cout << test_employee.s_name() << std::endl;    
    std::cout << test_employee.s_id() << std::endl;    

    Test< float > test_float;
    std::cout << test_float.s_name() << std::endl;    
    std::cout << test_float.s_id() << std::endl;    


    std::cin.ignore();
    return 0;
}

Выходы:

class Person
class Employee
class Person *
class Employee
class Test<int>
3462688
3462688
3462688
int
class Person
3421584
class Employee
3462504
float
3462872

Это работает по крайней мере на VC10Beta1 и VC9, должно работать на GCC. Кстати, чтобы использовать typeid (и dynamic_cast), вы должны разрешить информацию о типах во время выполнения на вашем компиляторе. Он должен быть включен по умолчанию. На некоторых плитах / компиляторах (я имею в виду некоторые встроенные аппаратные средства) RTTI не включен, потому что он имеет свою стоимость, поэтому в некоторых крайних случаях вам нужно будет найти лучшее решение.

2 голосов
/ 29 мая 2009

Вот что я в итоге сделал. Если у вас есть какие-либо отзывы (плюсы, минусы), пожалуйста, дайте мне знать.

<code>
template < class DERIVED >
class Foo
{
public:
    static const char* name(); // Derived classes will implement, simply<br>
                               // returning their class name
    static int s_id()
    {
        static const int id = Id_factory::get_instance()->get_id(name());
        return id;
    }
    // ...
};

По сути, идентификатор будет назначен после сравнения строк, а не сравнения указателей. Это не идеал с точки зрения скорости, но я сделал id статическим const, так что он будет рассчитываться только один раз для каждого производного.

2 голосов
/ 28 мая 2009

В моей предыдущей компании мы сделали это, создав макрос, который будет принимать имя класса в качестве параметра, создать локальную статическую переменную с уникальным идентификатором (на основе имени класса), а затем создать переопределение виртуальной функции, объявленной в базовый класс, который возвратил статический член. Таким образом, вы можете получить идентификатор во время выполнения из любого экземпляра иерархии объектов, аналогично методу getClass () в Java-объекте, хотя и гораздо более примитивному.

1 голос
/ 14 января 2016

Пока я не на 100% доволен ответами, и я разработал одно решение для меня. Идея состоит в том, чтобы вычислить хеш имени типа, используя typeinfo. Все выполняется один раз при загрузке приложения, поэтому нет никаких перегрузок во время выполнения. Это решение будет работать также с использованием разделяемых библиотек в качестве имени типа, и его хеш будет согласованным.

Это код, который я использую. Это прекрасно работает для меня.

#include <string>
#include <typeinfo>
#include <stdint.h>

//###########################################################################
// Hash
//###########################################################################
#if __SIZEOF_POINTER__==8
inline uint64_t hash(const char *data, uint64_t len) {
    uint64_t result = 14695981039346656037ul;
    for (uint64_t index = 0; index < len; ++index)
    {
        result ^= (uint64_t)data[index];
        result *= 1099511628211ul;
    }
    return result;
}
#else
inline uint32_t hash(const char *data, uint32_t len) {
    uint32_t result = 2166136261u;
    for (uint32_t index = 0; index < len; ++index)
    {
        result ^= (uint32_t)data[index];
        result *= 16777619u;
    }
    return result;
}
#endif
inline size_t hash(const std::string & str) { return hash(str.c_str(), str.length()); }

//###########################################################################
// TypeId
//###########################################################################
typedef size_t TypeId;

template<typename T>
static const std::string & typeName() {
    static const std::string tName( typeid(T).name() );
    return tName;
}
template<typename T>
static TypeId typeId() {
    static const TypeId tId = hash( typeName<T>() );
    return tId;
}
1 голос
/ 29 мая 2009

Что за удостоверение личности? Вы ищете атомно увеличивающийся int? Если строка в порядке, как насчет:

static string s_id()
{
   return typeid(Foo<DERIVED>).name();
}

Если оно должно быть целым числом, но не увеличиваться автоматически, вы можете хэшировать это для 128-битного целого числа, которое вряд ли будет иметь коллизии (хотя, вероятно, большее число, чем вам нужно)

1 голос
/ 28 мая 2009

Нет ничего стандартизированного. Кроме того, я не нашел взлома, который я нашел надежным.

Лучшее, что я смог придумать:

template < class DERIVED, int sid >
class Foo
{
    public:    
      static int s_id()    
      {        
          return sid;
      }    
};

Foo<MyClass, 123456>   derivedObject;
0 голосов
/ 31 марта 2019

Приведенный ниже фрагмент работает в VS (2015) и сборках Release:

template <typename T>
struct TypeId
{
  static size_t Get()
  {
    return reinterpret_cast<size_t>(&sDummy);
  }

private:
  static char sDummy;
};

template <typename T>
char TypeId<T>::sDummy; // don't care about value

Также испытано и протестировано на GCC v7.3 (Ubuntu 16.04) и LLVM v10.0.0 (Mac OS High Sierra).

Упражнение для читателя: по крайней мере типы const и ref должны иметь тот же идентификатор типа, что и необработанный тип.

0 голосов
/ 10 января 2018
#include <stdint.h>
#include <stdio.h>

#define DEFINE_CLASS(class_name) \
    class class_name { \
    public: \
        virtual uint32_t getID() { return hash(#class_name); } \

// djb2 hashing algorithm
uint32_t hash(const char *str)
{
    unsigned long hash = 5381;
    int c;

    while ((c = *str++))
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

DEFINE_CLASS(parentClass)

    parentClass() {};
    ~parentClass() {};
};

DEFINE_CLASS(derivedClass : public parentClass)

    derivedClass() : parentClass() {};
    ~derivedClass() {};
};

int main() {
    parentClass parent;
    derivedClass derived;
    printf("parent id: %x\nderived id: %x\n", parent.getID(), derived.getID());
}
0 голосов
/ 28 мая 2009

Вы можете сделать следующее:

#include <iostream>


template <int id = 5>
class blah
{
public:
    static const int cid = id; 
};

int main(int argc, char *argv[])
{
    std::cout << blah<>::cid << " " << blah<10>::cid << std::endl;

}

Я не знаю, хорошая ли это идея. Часть blah<> тоже немного не интуитивна. Возможно, вам лучше назначить им идентификаторы вручную, либо вы можете создать базовый тип, присвоив ему аргумент шаблона с идентификатором класса.

...