Showing posts with label traits. Show all posts
Showing posts with label traits. Show all posts

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

Thursday, September 27, 2012

Tablified Traits

Here is simple c++ trait wrapper that goes around the old standard c++ array of structs lookup table. It handles the forward and reverse lookup of the trait entry via its main key and an attrib and then the access of the various attributes from the resulting row.

There are some caveats to it. The Enum id cant be sparse. and the UNKNOWN entry needs to be second last before the MAX entry marker.

#include <iostream>

template<int LAST, typename R, typename T>
R lookup(T* table, R T::*member, int key) 
{
  if (key < LAST) return table[key].*member;
  return table[LAST].*member;
}

template<int LAST, typename R, typename T>
int locate(T* table, R T::*member, R target) 
{
  int k = 0;
  while (k < LAST)
    {
      if (table[k].*member ==  target)
 return k;
      k++;
    }
  return LAST;
}

template<int LAST, typename T>
int locate(T* table, const char* T::*member, const char* target) 
{
  int k = 0;
  while (k < LAST)
    {
      if (std::string(table[k].*member) == std::string(target))
 return k;
      k++;
    }
  return LAST;
}

class TableTrait
{
public:
  enum Key
    {
      ITEM1,
      ITEM2,
      ITEM3,
      ITEM4,
      ITEM5,
      UNKNOWN, //Must be second last
      MAX      //Must be last
    };    

private:  
  struct TableEntry
  {
    Key         key;
    int         attribA;
    bool        attribB;
    const char* attribC;
  };

  static TableEntry lookup_table[MAX];

  int key_;
public:
  TableTrait(int key) :
    key_(lookup<UNKNOWN>(lookup_table, &TableEntry::key, key))
  {}

  TableTrait(const char* rev_key) :
    key_(locate<UNKNOWN>(lookup_table, &TableEntry::attribC, rev_key))
  {}

  bool        valid()   { return key_ != UNKNOWN; }
  Key         key()     { return static_cast<Key>(key_); }
  int         attribA() { return lookup<UNKNOWN>(lookup_table, &TableEntry::attribA, key_); }
  bool        attribB() { return lookup<UNKNOWN>(lookup_table, &TableEntry::attribB, key_); }
  const char* attribC() { return lookup<UNKNOWN>(lookup_table, &TableEntry::attribC, key_); }  
};

TableTrait::TableEntry TableTrait::lookup_table[TableTrait::MAX] =
{
  { ITEM1,   3, false, "ITEM1"   },
  { ITEM2,   2, true,  "ITEM2"   },
  { ITEM3,   6, false, "ITEM3"   },
  { ITEM4,   5, true,  "ITEM4"   },
  { ITEM5,   8, false, "ITEM5"   },
  { UNKNOWN, 0, false, "UNKNOWN" }
};

int main()
{
  std::cout << "lookup: " << TableTrait(TableTrait::ITEM1).attribC() << "\n";
  std::cout << "lookup: " << TableTrait(TableTrait::ITEM2).attribB() << "\n";
  std::cout << "locate: " << TableTrait("ITEM3"          ).attribA() << "\n";
  std::cout << "locate: " << TableTrait("ITEM2"          ).key()     << "\n";
  std::cout << "locate: " << TableTrait("Blah"           ).attribC() << "\n";
}

Sunday, May 22, 2011

rtti to traits for auto uuiding classes

There are times when you want automatically UID or index a class. The rtti is string based and I am assuming that simple Hashing of of that string is not sufficient. So here is a quick hack up of trait class to achieve the result.

#include <iostream>
#include <stdint.h>
#include <map>
#include <typeinfo>

class RttiCasterBase
{
private:
  class RttiRegFactory
  {
  private:
    typedef std::map<std::string, uint32_t> RttiIDMap;
    
    //0 is the bad marker..
    uint32_t g_uid;
    RttiIDMap rttiIDMap;
    
    RttiRegFactory() :
      g_uid(1)
    {}
    
  public:
    static RttiRegFactory& instance()
    {
      static RttiRegFactory instance_;
      return instance_;
    }
    
    uint32_t getID(std::string rttiName)
    {
      if (rttiIDMap.find(rttiName) != rttiIDMap.end())
 return rttiIDMap[rttiName];
      rttiIDMap[rttiName] = g_uid;
      return g_uid++;
    }
  };
protected:
  static uint32_t  lookup(std::string rttiID)
  {
    return RttiRegFactory::instance().getID(rttiID);
  }
  
public:
  RttiCasterBase() {}
  virtual ~RttiCasterBase() {}
  
  virtual uint32_t      uid() = 0;
  virtual std::size_t   size() = 0;
  virtual std::string   rttiID() = 0;
};

template<typename T>
class RttiCasterImp : public RttiCasterBase
{
public:
  typedef T Type;
  uint32_t uid_;
  
  RttiCasterImp() :
    RttiCasterBase()
  {
    uid_ = RttiCasterBase::lookup(typeid(T).name());
  }
  
  ~RttiCasterImp() {}

  virtual uint32_t uid()
  {
    return uid_;
  }
  
  virtual std::size_t size()
  {
    return sizeof(T);
  }
  
  virtual std::string   rttiID()
  {
    return typeid(T).name();
  }
  
  T* cast(void* buf)
  {
    return reinterpret_cast<T*>(buf);
  }
  
  void print(std::ostream& out)
  {
    out << "uid:  "   << uid()    << std::endl
 << "size: "   << size()   << std::endl
 << "rttiID: " << rttiID() << std::endl
 << std::endl;
  }
};

class A {};
class B { int var; };
class C { char stuff[10]; };

int main(int argc, char const * const *argv)
{
  RttiCasterImp<A> InfoA;
  RttiCasterImp<B> InfoB;
  RttiCasterImp<C> InfoC;
  RttiCasterImp<B> InfoD;
  
  InfoA.print(std::cout);
  InfoB.print(std::cout);
  InfoC.print(std::cout);       
  InfoD.print(std::cout);       
}

Output looks like:
$ a.exe
uid:  1
size: 1
rttiID: 1A

uid:  2
size: 4
rttiID: 1B

uid:  3
size: 10
rttiID: 1C

uid:  2
size: 4
rttiID: 1B

Saturday, June 26, 2010

c++: Basic Traits

Traits:
In C++ "traits" are basically a group of template classes that provide miscellaneous information about another type or data structure.

'Think of a trait as a small object whose main purpose is to carry information used by another object or algorithm to determine "policy" or "implementation details".'
- Bjarne Stroustrup

Traits are very common in C++. One of the more common ones is the string class its self. The string class uses traits to provide information about internationalization and character encoding. Often they are visible in the code and debugger output as a template parameter that has a default initialization via a second template.

In other cases the use of traits is more normal as in the the class "std::numeric_limits" and the other members of the limits.h header.

Boost also offers many interesting and helpful traits classes as an example here is the "is_void" traits class.

template< typename T > 
struct is_void{ 
  static const bool value = false;
};

template<> 
struct is_void< void >{ 
  static const bool value = true; 
};


As you can see this trait template's sole purpose to is provide information about what the input parameter type was. This is only really useful in a parts of the code which the programmer doesn't know what the input type was: Hence inside another template.

For more info refer to:
http://www.cantrip.org/traits.html
http://www.cplusplus.com/reference/std/limits/numeric_limits/