Saturday, October 5, 2013

hashed RTTI and Mixin's for Semi-UUID taging class

Quite a while back I posted some code that uses RTTI to UUID tag classes. In the post i mention that a hash on the RTTI info can be used to also ID the class. Here ere are a few implementations that use std::hash to get a semi-unique ID and automatically apply it to a class.

There are 2 main methods demoed here;
  • A template constructor with pointer approach called "AutoIDPtr" which is what you want if your classes have many layers of inheritance like the "CChild" example
  • A CRTP Mixin approach call "AutoIDMixin" is also possible and cuts out the mess on the stack at runtime, BUT this prevents any more derivation on the class. It also means that you have to keep typing out the instantiated class name in all the constructors as well which gets annoying.
  • A final way that is not shown is to use pass down a summary of results in a AutoIDBase class and not derive from the AutoID.. classes at all.
Keep in might that hash has a chance of clashing, if this happens you can always specialize the Attribs/ClassDictionary class. Also the ClassDictionary is completely optional. You can simply delete it use the Attrib Traits ID inplace of the REG_ID at get a working system.
#include <iostream>
#include <iomanip>
#include <stdint.h>
#include <typeinfo>
#include <functional>
#include <map>

/****************************************************************
 ****************OPTIONAL INFO GATHERING PLACE*******************
 ****************************************************************/

struct ClassDictionary
{
    struct Reg
    {
        uint32_t    id_;
        std::string name_;
        uint32_t    hash_;
    };

    typedef std::map<std::string, Reg> RegMap;

    template <typename T>
    static uint32_t reg()
    {
        std::string key  = typeid(T).name();
        uint32_t    hash = std::hash<std::string>()(typeid(T).name());

        std::cout << "Registering hash:"
                  << std::hex << std::setw(8) << hash << std::dec
                  << " as "  << key
                  << "\n";

        return regCore(key,hash);
    }

    static uint32_t regCore(std::string key, uint32_t hash)
    {
        RegMap::iterator rit = regMap_.find(key);
        if (rit != regMap_.end())
            return rit->second.hash_;

        regMap_[key].id_ = gID++;
        regMap_[key].name_ = key;
        regMap_[key].hash_ = hash;

        return regMap_[key].hash_;
    }

    static void printList()
    {
        std::cout << "ClassDictionary has " << gID << " entries\n";
        for (const auto& e : regMap_ )
        {
            std::cout << "Entry:" << e.second.id_
                      << " Hash:"
                      << std::hex << std::setw(8) << e.second.hash_ << std::dec
                      << " Name:" << e.second.name_
                      << "\n";
        }
    }

    static uint32_t gID;
    static RegMap regMap_;
};

uint32_t                ClassDictionary::gID = 0;
ClassDictionary::RegMap ClassDictionary::regMap_;

/****************************************************************
 ***************************THE CORE*****************************
 ****************************************************************/

template <typename T>
struct Attrib
{
    static const uint32_t REG_ID;
    static const char*    NAME;
    static const uint32_t ID;
    enum { SIZE = sizeof(T) } ;
};

template<typename T>
const char* Attrib<T>::NAME = typeid(T).name();

template<typename T>
const unsigned Attrib<T>::ID = std::hash<std::string>()(typeid(T).name());

template<typename T>
const uint32_t Attrib<T>::REG_ID = ClassDictionary::reg<T>();

class Base
{
public:
    Base(uint32_t type, uint32_t size) :
        type_(type),
        size_(size)
    {}

    void whoami()
    {
        std::cout << "I am type:"
                  << std::hex << std::setw(8) << type_ << std::dec
                  << " size:" << size_
                  << "\n";
    }

private:
    uint32_t type_;
    uint32_t size_;
};

class AutoIdPtr : public Base
{
public:
    template <typename T>
    AutoIdPtr(T* kid) :
      Base(Attrib<T>::REG_ID, Attrib<T>::SIZE)
    {}
};

template <typename T>
class AutoIdMixin : public Base
{
public:
    AutoIdMixin() :
      Base(Attrib<T>::REG_ID, Attrib<T>::SIZE)
    {}
};

/****************************************************************
 ***************************TEST IT******************************
 ****************************************************************/

class A : public AutoIdMixin<A>
{
public:
    A() : AutoIdMixin<A>()
    {}
};

class B : public AutoIdPtr
{
public:
    B() :
        AutoIdPtr(this)
    {}

    int var;
};

class C : public AutoIdPtr
{
public:
    C() :
        AutoIdPtr(this)
    {}

    template <typename T>
    C(T* child) :
        AutoIdPtr(child)
    {}

    char stuff[10];
};

class CChild : public C
{
    CChild() : C(this)
    {}
};

class IHaveALongAndMessyName : public AutoIdPtr
{
public:
    IHaveALongAndMessyName() :
        AutoIdPtr(this)
    {}
};

int main()
{
    A a;
    B b;
    C c;
    C c2;

    a.whoami();
    b.whoami();
    c.whoami();

    ClassDictionary::printList();
}
The output looks like
Registering hash:85b5dc34 as 1A
Registering hash:60a08ea3 as 1B
Registering hash:d9107b5c as 1C
Registering hash:59e2da5b as 22IHaveALongAndMessyName
Registering hash:52593138 as 6CChild
I am type:85b5dc34 size:8
I am type:60a08ea3 size:12
I am type:d9107b5c size:20
ClassDictionary has 5 entries
Entry:0 Hash:85b5dc34 Name:1A
Entry:1 Hash:60a08ea3 Name:1B
Entry:2 Hash:d9107b5c Name:1C
Entry:3 Hash:59e2da5b Name:22IHaveALongAndMessyName
Entry:4 Hash:52593138 Name:6CChild

No comments:

Post a Comment