M
M
Monnoroch2012-07-15 22:11:32
Programming
Monnoroch, 2012-07-15 22:11:32

RTTI vs template magic?

There is a class structure:

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 */ }
}

And there is a class
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;
}

And there is some wrapper on RTTI, which is designed to make it so that the NamedObject can be perceived as an object lying under it:
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;
}

And there is some feeling that this scheme can be implemented without RTTI, on templates and multiple inheritance.
The code, in a sense, is library code, the factory is engaged in the creation of all kinds of objects of this hierarchy, so creepy template magic is acceptable.
If anyone wants to break his head and has a great C ++ skill, I invite you to discuss possible options.

Answer the question

In order to leave comments, you need to log in

9 answer(s)
N
NYM, 2012-07-15
@NYM

Trying to write your own component model in C++
Will it work?

R
Ryadovoy, 2012-07-16
@Ryadovoy

From the description it is not clear what the ultimate goal is, what exactly do you want to do?

C
cencio, 2012-07-16
@cencio

if you understand correctly what you are trying to do, then ciaranm.wordpress.com/2010/05/24/runtime-type-checking-in-c-without-rtti/ is the same, but written more accurately and in 2 versions, you need the last one ( at the end of the article), roughly speaking, a unique integer is generated for each type, and the type is checked using it.

G
Gribozavr, 2012-07-16
@gribozavr

LLVM and Clang use something similar. Here is the header implementing isa and dyn_cast llvm.org/docs/doxygen/html/Casting_8h_source.html
To support this in your classes you need to implement two classof() methods like clang.llvm.org/doxygen/Comment_8h_source.html
To support using objects -wrapper implement those template specialization simlify_type.

M
motl, 2012-07-16
@motl

You want to work with objects through a pointer to the IObject base class, and you want to implement isA using templates. This is not possible, since template instantiation occurs at compile-time, while IObject pointers are initialized at run-time. IObject is passed by pointer as an argument to the NamedObject constructor, so the obj type is known only at run-time and RTTI is indispensable here.

N
NYM, 2012-07-16
@NYM

We take the idea mentioned by the link in the posts above and make an example for it:

#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;
}

For such a cast to work, inheritance from a certain interface is required, but let's not forget that dynamic_cast also does not work for free, it requires a virtuality fee, i.e. your class must contain at least something virtual, even a destructor, but the type must be polymorphic.

A
AxisPod, 2012-07-16
@AxisPod

If you want to load dynamically, then template magic is not the way to go. This will require the union of namespaces, which will eventually require dynamically linked libraries to give unique names among all modules.
I did not refuse inheritance at home, because template magic, even if after a bunch of perversions and gives something, then there is a loss of development time, high code complexity.
I don't know what exactly you want. So in general I will try to tell my approach. Implemented an elementary mechanism. There is a basic interface, empty. All others inherit from it. The dynamic library implements 4 mandatory methods CreateInstance, DestroyInstance - for creating and deleting objects and CreateEnumerator, DestroyEnumerator - for creating an enumeration of available objects within the library. I made a simple library manager, a factory and an object of the shared_ptr type, only with a link to the library. The result is a simple code
Instance<IInterface> obj = mgr->CreateInstance<IInterface>("identificator");
CreateInstance uses dynamic_cast to cast to the desired one, in case of an error it throws an exception.

A
AxisPod, 2012-07-16
@AxisPod

In general, he is still interested in why his own implementation of a simple RTTI, which already exists.

V
Vladislav, 2012-07-18
@Wyrd

Here's another option for you:
pastebin.com/RZAY789w
can:

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 also implements IObject
FastObjectCast semantically equivalent to dynamic_cast, but 10 times faster (dynamic_cast can also be used).
p.s.
You can't do this:
template < class  T >  T  *  As ( )  {  return  ( T * ) this ;  }
You need to use static_cast, because after the cast, the pointer can change the value by a couple of bytes (this is due to multiple inheritance, if it exists in the hierarchy).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question