Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 16.06.2012, 23:40   #1
brag
Новичок
Джуниор
 
Регистрация: 16.06.2012
Сообщений: 6
По умолчанию Уникальный идентификатор типа

Добое время суток!
Нужно получить уникальный идентификатор класса без использования RTTI. Механизм должен быть thread safe, глобальные (и static в том числе) переменные запрещены.
Пока родил такой код
Код:
class basewrapper{
public:
	virtual int id()=0;
};

template<class T> class wrapper :public basewrapper{
public:
	explicit wrapper(T *msg) :basewrapper(),pbody(msg) {}
	static int getClassId(){ return reinterpret_cast<int>(getClassId); }
	
	virtual int id(){ return getClassId(); }
	
	T* body()const{ return pbody; }
	
private:
	T *pbody;
};

class msg1{};
class msg2{};

void test(){
	msg1 m1;
	msg2 m2;
	wrapper<msg1> wmsg1(&m1);
	wrapper<msg2> wmsg2(&m2);
	
	printf("%d %d;\n",wmsg1.id(),wmsg2.id());
	printf("%d %d;\n",wrapper<msg1>::getClassId(),wrapper<msg2>::getClassId());
}
На gcc работает на ура. Но есть ли гарантия, что линкер в одни прекрасный момент не сольет функции getClassId() шаблонов разных типов в одну?
brag вне форума Ответить с цитированием
Старый 17.06.2012, 01:53   #2
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

хм... с какой стати он это сделает?
_Bers вне форума Ответить с цитированием
Старый 17.06.2012, 02:46   #3
brag
Новичок
Джуниор
 
Регистрация: 16.06.2012
Сообщений: 6
По умолчанию

Ну хз, одинаковые строки ж умеет удалять. но тут шаблонные функции, да еще с разными именами. по идее не должен трогать.
Проверил на разных компилерах с разными опциями оптимизации (в том числе GCC LTO) - везде работало корректно.
brag вне форума Ответить с цитированием
Старый 17.06.2012, 04:02   #4
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Ну просто разные инстансы шаблона - это принципиально разные типы жеж.
А значит, каждый такой инстанс должен обладать собственной независимой копией getClassId()

Которая, к тому же используется (берётся адрес). Не должен он там ничего такого соптимизировать)

Можно вопрос: для чего поделка предназначена?
_Bers вне форума Ответить с цитированием
Старый 17.06.2012, 06:05   #5
brag
Новичок
Джуниор
 
Регистрация: 16.06.2012
Сообщений: 6
Сообщение

Та да, везде работает гуд, успел проверить на куче разного кода и интересующих компилерах...

Нужно для идентификации типов сообщений, чтобы руками не городить проверку типа сообщения и явное преобразование типов. И в других местах, где нужна простая и легковесная рантайм идентификация типов.
Простой пример использования
Код:
...
	static T* getObjptr(const basewrapper *iwrapper){
		if(iwrapper->id()==wrapper<T>::getClassId()){
			return static_cast<const wrapper<T>*>(iwrapper)->body();
		}else return 0;
	}
.....
void SomeClass::on_message(basewrapper *wrp){
	KeyMessage *km=wrapper<KeyMessage>::getObjptr(wrp);
	if(km)printf("Mesage from keyboard, scancode=%d\n",km->scancode());
}
Проект под embedded(проц Cortex-M3, памяти мало), посмеу ограничения на dynamic_cast итп и приложение многопоточное.
brag вне форума Ответить с цитированием
Старый 17.06.2012, 14:27   #6
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Вообще идея отличная! Я в восторге)

Только в basewrapper виртуальный диструктор просится.

Последний раз редактировалось _Bers; 17.06.2012 в 14:31.
_Bers вне форума Ответить с цитированием
Старый 17.06.2012, 15:38   #7
brag
Новичок
Джуниор
 
Регистрация: 16.06.2012
Сообщений: 6
По умолчанию

Да в embedded приходится придумывать всякое Виртуальный деструктор там есть, просто код еще драфт выложил, там и имена другие теперь...

Еще вариант один родил, без виртуальных функций с таким же поведением, немного экономичнее в плане процессорного времени
Код:
class IRtClassId{ // interface
public:
	explicit IRtClassId(unsigned id) :classid(id) {}
	unsigned id()const{ return classid; }
private:
	unsigned classid;
};

template<class T> class RtClassId: public IRtClassId{
public:
	explicit RtClassId(T*) :IRtClassId(getClassId()) {}
	static int getClassId(){ return reinterpret_cast<unsigned>(getClassId); }
	static T* getObjectptr(IRtClassId *p){
		if(p->id()==getClassId())return static_cast<T*>(p);
		else return 0;
	}
};
Тест
Код:
class c1: public RtClassId<c1>{
public:
	c1() :RtClassId(this) {}
};
class c2: public RtClassId<c2>{
public:
	c2() :RtClassId(this) {}
};

void ztst1(IRtClassId *wrp){
	printf("%d %d\n",
		RtClassId<c1>::getObjectptr(wrp),
		RtClassId<c2>::getObjectptr(wrp));
}

void ztest(){
	c2 w1;
	ztst1(&w1);
}

out:
0 536888040
Указатель this в RtClassId передаем, чтобы компилятор запретил такую ситуацию (при копипасте кода может возникнуть )
Код:
class c2: public RtClassId<c1>{
public:
	c2() :RtClassId(this) {}
};
Конечно вариант, когда c2 наследник c1 пролетит мимо, но такое врядли возможно идеологически Типы сообщений наследуются от какого-то базового класса, а не один от другого.
И в отличии от dynamic_cast данные реализации по сути не поддерживают наследования:
RtClassId<наследник>::getObjectptr( наследник) всегда возвращает 0, если налседник задекларирован как
Код:
class базовый: public RtClassId<базовый>;
class наследник: public базовый;
С dynamic_cast такое проконало бы. Но оно и не нужно, такой вариант вполне какит при разработке приложений под микроконтроллеры:
Код:
class базовый;
class наследник: public базовый, public RtClassId<наследник>;

Последний раз редактировалось brag; 17.06.2012 в 15:52.
brag вне форума Ответить с цитированием
Старый 17.06.2012, 15:54   #8
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2brag
2 замечания
Цитата:
static int getClassId(){ return reinterpret_cast<unsigned>(getClass Id); }
1. указатель может не влезть в int (x86-64 тому пример). нужно использовать intptr_t.
2. я бы переписал так
Код:
static intptr_t id()
{
	static const int dummy = 0;
	return reinterpret_cast<intptr_t>(&dummy);
}
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance

Последний раз редактировалось pproger; 17.06.2012 в 15:57.
pproger вне форума Ответить с цитированием
Старый 17.06.2012, 16:29   #9
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Вопрос: можно ли добиться след. эффекта:

Код:
template<class T>
class Test
{
    enum { eID =....}; //автоматическая генерация уникального для типа айдишника
}
_Bers вне форума Ответить с цитированием
Старый 17.06.2012, 17:43   #10
brag
Новичок
Джуниор
 
Регистрация: 16.06.2012
Сообщений: 6
По умолчанию

Цитата:
1. указатель может не влезть в int (x86-64 тому пример). нужно использовать intptr_t.
Код изнчально ориентирован под 32разрядную(иногда и 8,16) архитектуру. Хотя конечно будет не лишним, но привычка к стандартным типам еще с недалеких времен, когда писал на чистом C берет свое

Цитата:
2. я бы переписал так
Код:
static intptr_t id()
{
static const int dummy = 0;
return reinterpret_cast<intptr_t>(&dummy);
}
Ну можно и так, вроде сильно разницы нету. или есть?

_Bers, хм, надо подумать...
brag вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Уникальный GUI интерфейс |{ot Общие вопросы C/C++ 6 03.07.2011 18:25
Уникальный идентификатор (Hardware ID) Marx88 Общие вопросы Delphi 5 05.07.2010 15:11
Как делают уникальный идентификатор строки в БД? sergey113 Помощь студентам 3 05.08.2008 17:48
Уникальный идентификатор romets Win Api 9 03.02.2008 02:30