3

I understand that templates are compile-time, and typeinfo-related are runtime, but I'm wondering if I can achieve my particular task.

I have a factory method using templates to create objects of a particular type; I also have a preloader (reading data from disk), which determines what type of object is to be created, but doesn't actually create it - that's the responsibility of the creator, and is executed on demand.

void Creator::Spawn(Preloader* pl)
{
    std::unordered_map<size_t, std::type_index>   hashmap;

    // assume ObjectType is simply a wrapper around a hash
    hashmap[ObjectType<Type1>::GetType().Hash()] = typeid(Type1);
    hashmap[ObjectType<Type2>::GetType().Hash()] = typeid(Type2);

    for ( auto& const i : pl->GetPreloadInfo() )
    {
        size_t  hash = i->type_hash.Hash();

        // similar-to-desired usage
        FactoryCreate<hashmap[hash]>();
    }
}

Is there any way to achieve this? Obviously I can do manual checks for each, like below, but it's nasty at best.

        // poor, manual implementation
        if ( hash == ObjectType<Type1>::GetType().Hash() )
            FactoryCreate<Type1>();
        else if ( hash == ObjectType<Type2>::GetType().Hash() )
            FactoryCreate<Type2>();

Everything I've tried so far has hit the runtime vs compile-time differences, though I'm definitely not aware of all the newest C++11 tricks that may assist (C++14 not usable).

Partially related question here: Use data type (class type) as key in a map

1 Answer 1

2

Assuming the hash part is set in stone, you can create a map from those type hashes to your factory functions directly:

using map_type = std::unordered_map<size_t, std::function<void()>>;

template <class... Ts>
map_type create_hash_map() {
    map_type map;

    // emplace (hash<T>, FactoryCreate<T>) for each T
    using swallow = int[];
    (void)swallow{0,
        (void(
            map.emplace(ObjectType<Ts>::GetType().Hash(),
                []{ FactoryCreate<Ts>(); }
                )
        ), 0)...
    };

    return map;
}

Then we can just use that map directly:

void Creator::Spawn(Preloader* pl)
{
    static auto hashmap = create_hash_map<Type1, Type2>();

    for ( auto& const i : pl->GetPreloadInfo() )
    {
        size_t  hash = i->type_hash.Hash();
        hashmap[hash]();
    }
}

That doesn't have error-checking, so if the hash isn't actually in the map, you'll get a bad_function_call exception from std::function. If you need error checking, you can instead do a lookup in the map first:

auto it = hashmap.find(hash);
if (it != hashmap.end()) {
    (it->second)(); 
}
else {
    // error!
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.