开发者

Is it possible to generate constant value during compilation?

I would like my classes to be identified each type by an unique hash code. But I don't want these hashed to be generated every time a method, eg. int GetHashCode(), is invoked during runtime. I'd like to use already generated constants and I was hoping there is a way to make the compiler do some come computing and set these constants. Can it be done using templates? Could you give me some example, if it is possible.

UPDATE:

Thanks to kriss' comment I realized my question should go like this: How to do the type checking with the lowest runtime cost possible?

I'd like to check a pointer to an object against a class type. Just the classes I implement in my libs, so I was thinking of some custom hashing, thus the original question. I did consider using typeid but I am unaware of the runtime cost of using it. I made an assumption that since typeid produces a type_info class that would be more consuming than simp开发者_C百科le comparision of unique int values.


You can do it with boost.MPL.


I would go simple route:

  • For classes that would be static property - so just pick a number for each class.
  • For instances - just use the address.


Static const's are evaluated at compile time - which is pretty much the basis for metaprogramming at large. Moreover, type_info::hash_code is particularly adapted for your needs, so try -

class MyClass
{
static const size_t TypeHashCode = typeid(MyClass).hash_code();
...
}

(I'm not around a compiler right now, so this may take some refining. Will try and recheck tomorrow)

EDIT: indeed, it is not only MS specific but also added only in VS2010 - but hey, at least MS agrees this is a valid need. If you don't allow both VS2010 and boost in your code - you're pretty much left with the standard compliant facilities: typeid or dynamic_cast. They do incur some overhead, but I'd take extra care to verify this overhead is indeed a worthy battle. (my money goes to - not.)


All such classes share something common. Then why not add a symbolic constant in a common enum for each one, you'll leave the enum give values for you, it's easier than giving explicit constants (you still have to declare each class in the enum).


template<class T>
struct provide_hash_code_for_class
{
  public:
    static uintptr_t GetHashCode()
    {
      return(reinterpret_cast<uintptr_t>(&unused));
    }
  private:
    static void *unused;
};

template<class T>
void *provide_hash_code_for_class<T>::unused;

class MyClass : public provide_hash_code_for_class<MyClass>
{
};

int main()
{
  std::cout << std::hex << MyClass::GetHashCode() << std::endl;
  std::cout << std::hex << MyClass().GetHashCode() << std::endl;
  return(0);
}

Please be aware that hash codes will change between runs so you can't rely on them for example for interprocess communication.


building on the simple route route by Nikolai N Fetissov:

  • For classes that would be static property - use the address of a function cast to an intptr_t to give a unique yet compiled-in value.
  • For instances - just use the address.


It is a pitty that there is no compile-time type hash_code supported by the standard. As a workaround, one can generate a compile time hash from the class name. An example below.

#include <stdint.h>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <cassert>

//Compile-time string hashing.
class HashedString
{
public:
    typedef int64_t HashType;
    explicit constexpr HashedString(const char* str):  m_hash(hashString(str)) {}

    static inline constexpr HashType hashString(const char* str)
    {
        return ( !str ? 0 : hashStringRecursive(5381, str));
    }
    static inline constexpr HashType hashStringRecursive(HashType hash, const char* str)
    {
        return ( !*str ? hash : hashStringRecursive(((hash << 5) + hash) + *str, str + 1));
    }
    const HashType m_hash;
};

struct EventBase
{
    using IdType = HashedString::HashType;

    virtual ~EventBase() {}
    IdType getId() const {  return m_eventId;  }       //present the runtime event id

    EventBase(IdType myId) : m_eventId { myId } { }

    template<class DerivedEvent>
    const DerivedEvent* getAs() const
    {
        return dynamic_cast<const DerivedEvent*>(this);
    }

protected:
    const IdType m_eventId;
};

#define DEFINE_EVENT_ID(className) \
static constexpr IdType id = HashedString(#className).m_hash;   \

struct SomeEvent1 : public EventBase
{
    DEFINE_EVENT_ID(SomeEvent1);

    SomeEvent1(int status) : EventBase(id), m_status { status } { assert(id == m_eventId);  }
    int m_status;
};


struct SomeEvent2 : public EventBase
{
    DEFINE_EVENT_ID(SomeEvent2);

    SomeEvent2() : EventBase(id) { assert(id == m_eventId); }
    std::string m_s = "test event 2";
};

void testEvents()
{
    std::vector<std::shared_ptr<EventBase>> events;

    events.push_back(std::make_shared<SomeEvent1>(123));
    events.push_back(std::make_shared<SomeEvent2>());

    for (auto event : events) {
        switch(event->getId()) {
            case SomeEvent1::id:
                std::cout << "SomeEvent1 " << event->getAs<SomeEvent1>()->m_status << std::endl;
                break;
            case SomeEvent2::id:
                std::cout << "SomeEvent2 " << event->getAs<SomeEvent2>()->m_s << std::endl;
                break;
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜