RTTI vs Шаблонная магия?

Есть структура классов:

class IObject {
public:
	virtual bool Method() = 0;
	template<class T> T * As() { return (T*)this; }	// да-да, не dynamic_cast.
}

class Object1 : public IObject {
	public: virtual bool Method() { /* some logic */ }
}

class Object2 : public IObject {
	public: virtual bool Method() { /* some logic */ }
}



И есть класс
class NamedObject : public IObject {
public:
	NamedObject(const std::string& nm, IObject * o) : name(nm), obj(o) {}

	virtual bool Method() { return obj->Method(); }
	std::string& Name() { return name; }
	IObject * Object() { return obj; }
	
private:
	IObject * obj;
	std::string name;
}



И есть некоторая обертка на RTTI, которая призвана сделать так, что NamedObject может восприниматься, как лежащий под ним обьект:
template<typename T> 
inline bool isA(IObject * obj)
{
	return dynamic_cast<T*>(obj) != null ||
		(isA<NamedObject>(obj) && isA<T>(obj->As<NamedObject>()->Object()));
}

template<> 
inline bool isA<NamedObject>(IObject * obj)
{
	return dynamic_cast< NamedObject* >(obj) != null;
}



И есть некое чувство, что эту схему можно реализовать без RTTI, на шаблонах и множественном наследовании.

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


Если кто желает поломать голову и имеет большой C++ скилл, приглашаю обсудить возможные варианты.
  • Вопрос задан
  • 5706 просмотров
Пригласить эксперта
Ответы на вопрос 9
Ryadovoy
@Ryadovoy
Из описания непонятно какая конечная цель, что именно Вы хотите сделать?
Ответ написан
@cencio
если правильно понял, что пробуете сделать, то ciaranm.wordpress.com/2010/05/24/runtime-type-checking-in-c-without-rtti/ тоже самое, но акуратней написаное и в 2 вариантах, вам нужен последний(в конце статьи), грубо говоря там для каждого типа генерится уникальное целое число, по нему и происходит проверка типа.
Ответ написан
Комментировать
@gribozavr
В LLVM и Clang используется нечто подобное. Вот заголовок, реализующий isa и dyn_cast llvm.org/docs/doxygen/html/Casting_8h_source.html
Для поддержки этого в ваших классах нужно реализовать два метода classof(), например clang.llvm.org/doxygen/Comment_8h_source.html

Для поддержки использования объектов-обёрток реализуй те специализацию шаблона simlify_type.
Ответ написан
Комментировать
@motl
Вы хотите работаете с объектами через указатель на базовый класс IObject и хотите реализовать isA при помощи шаблонов. Это невозможно, так как инстанцирование шаблонов происходит в compile-time, а инициализация указателей на IObject происходит в run-time. IObject передается по указателю в качестве аргумента в конструктор NamedObject, поэтому тип obj известен только в run-time и без RTTI тут не обойтись.
Ответ написан
@NYM
Берем идею, упомянутую ссылкой в постах выше и делаем для нее пример:
#include <iostream>

class TypeIdGenerator
{
public:
	template <typename T>
	static const int GetId()
	{
		static const int Id = GetNextId();
		return Id;
	}
private:
	static int GetNextId()
	{
		static int Id = 0;
		return Id++;
	}
};

struct ICast
{
	virtual ~ICast()
	{
	}
	virtual void const* Cast(int id) const = 0;
	virtual void * Cast(int id) = 0;
};

template <typename T>
class CastImpl
	: public T
{
public:
	virtual void const* Cast(int id) const
	{
		return id == TypeIdGenerator::GetId<T>() ? this : 0;
	}
	virtual void * Cast(int id)
	{
		return id == TypeIdGenerator::GetId<T>() ? this : 0;
	}
};

template <typename T>
T* MyCast(ICast *obj)
{
	return reinterpret_cast<T *>(obj->Cast(TypeIdGenerator::GetId<T>()));
}

template <typename T>
T const* MyCast(ICast const *obj)
{
	return reinterpret_cast<T const *>(obj->Cast(TypeIdGenerator::GetId<T>()));
}

class A
	: public ICast
{
public:
	void mtd() const
	{
		std::cout << __FUNCTION__ << std::endl;
	}
};

int main()
{
	ICast const &obj = CastImpl<A>();
	A const *a = MyCast<A>(&obj);
	if (a)
		a->mtd();
	return 0;
}

Для работы такого каста нужно наследование от некого интерфейса, но не будем забывать, что dynamic_cast тоже не бесплатно работает, он требует плату виртуальностью, т.е. ваш класс должен хоть что-то содержать виртуальное, хоть деструктор, но тип должен быть полиморфным.
Ответ написан
AxisPod
@AxisPod
Если вы хотите загружать динамически, то шаблонная магия штука неподходящая. При этом понадобится объединение пространств имен, что в итоге потребует в динамически подключаемых библиотеках давать уникальные имена среди всех модулей.

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

Не знаю что конкретно вы хотите. Так что в общем попытаюсь рассказать свой подход. Реализовал элементарный механизм. Есть базовый интерфейс, пустой. От него наследуются все остальные. Динамическая библиотека реализует 4 обязательных метода CreateInstance, DestroyInstance — для создание и удаления объектов и CreateEnumerator, DestroyEnumerator — для создания перечисления доступных объектов внутри библиотеки. Сделал простенький менеджер библиотек, фабрику и объект по типу shared_ptr, только с завязкой на библиотеку. В итоге используется простой код

Instance<IInterface> obj = mgr->CreateInstance<IInterface>("identificator");

CreateInstance использует dynamic_cast для приведения к нужному, в случае ошибки кидает исключение.
Ответ написан
Комментировать
AxisPod
@AxisPod
А вообще все же интересует, зачем своя реализация простого RTTI, которое и так уже есть.
Ответ написан
Комментировать
Wyrd
@Wyrd
Архитектор
Вот вам еще вариант:
pastebin.com/RZAY789w

умеет:
typedef std::unique_ptr< IObject > ptr;
 
ptr p = CreateObject< Object3 >();
assert( FastObjectCast< Object3 >( p.get() ) );
assert( FastObjectCast< Object1 >( p.get() ) ); // Object3 inherits Object1
assert( !FastObjectCast< INamedObject >( p.get() ) ); // p is unnamed Object3
 
ptr q = CreateObject< Object3 >( "some name" );
assert( FastObjectCast< Object3 >( q.get() ) );
assert( FastObjectCast< Object1 >( q.get() ) ); // Object3 inherits Object1
assert( FastObjectCast< INamedObject >( q.get() ) ); // p is named Object3
INamedObject наследуется также реализует IObject
FastObjectCast семантически эквивалентен dynamic_cast, но раз в 10 быстрее (dynamic_cast тоже можно использовать).

p.s.
Так нельзя делать:
template<class T> T * As() { return (T*)this; }
Нужно использовать static_cast, т.к. после каста указатель может поменять значение на пару байт (это происходит из-за множественного наследования, если оно есть в иерархии).
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы