简体   繁体   中英

How do I implement return type lookup for templates using a map in c++?

I have two arrays. One containing pointers to a base class. The other containing an Enum whose values give pointers as to the real type.

I would now like to have a function that can get me an array with those entries that have a certain Enum value associated with them and return that array with the correct return type.

Example (semi pseudocode):

std::array<BaseType, 2> a = { TypeAInstance, TypeBInstance };
std::array<TypeEnum, 2> b = { TypeA, TypeB };

template<typename SearchType>
std::array<SearchType*, 2> GetEntriesOfType()
{
  std::array<SearchType*, 2> ret;
  for(int i = 0; i < 2; i++) 
  {
    ret[i] = nullptr;
    if(b[i] == EnumForType(SearchType)) ret[i] = a[i];
  }
}

However I do not know of anyway to construct the EnumForType function or the reverse TypeForEnum function which would allow me to declare the template return type using:

template<SearchTypeEnum>
TypeForEnum(SearchTypeEnum) GetEntriesOfType();

I would like to define such a constexpr function if possible.

The underlying problem is that I have an array of Plugins that I iterate over. Where each plugin needs to be handled differently depending on the type. However since the type is runtime dependent and I can't have dynamic allocation (embedded system constraints) I store all Plugins in a fixed prereserved memory space. I need to somehow cast the pointer to the base class to its correct type during runtime and handle it accordingly.

This GetEntriesOfType function is supposed to make that more convenient. I could of course get both arrays and then do a switch statement while iterating over both but I wanted to simplify that to the end user of that library.

One way is to map each class type to an enumerator value:

class A;
class B;

enum class Types {
    A,
    B
};

template<class T> struct EnumForType;

template<> struct EnumForType<A> { static constexpr Types value = Types::A; };
template<> struct EnumForType<B> { static constexpr Types value = Types::B; };

int main() {
    auto a = EnumForType<A>::value;
    auto b = EnumForType<B>::value;
}

You would use it like:

if(b[i] == EnumForType<SearchType>::value)

Instead of a enum, you could create an id for each type.

Let's define this id for a type with templates. Since you prefer constexpr, RTTI is not a option. Here's what I do when I want a type id:

template<typename>
void type_id() {}

using type_id_t = void(*)();

This is as simple as that. Each instanciated function will ahve a different address, and the address of the function will always be the same for a given type under the as if rule.

Then, adding you metadata is pretty easy. We could define your array of plugin like this:

std::array<std::pair<type_id_t, BaseType*>, 2> a = {
    std::make_pair(type_id<TypeA>, typeAInstance),
    std::make_pair(type_id<TypeB>, typeBInstance)
};

With that, you just associated a instance with a value that identify the type. Your GetEntriesOfType become trivial to implement:

template<typename SearchType>
std::array<SearchType*, 2> GetEntriesOfType() {
    std::array<SearchType*, 2> ret;

    for(int i = 0; i < 2; i++) {
        ret[i] = nullptr;
        if(a[i].first == &type_id<SearchType>) ret[i] = a[i].second;
    }
}

I usually avoid to associate type with enums, as enums are not meant to represent type, but to map to simple integral values.

Plus, if your array is constexpr, you could even return an array with only the right size to fit all instance of the SearchType instead of filling other values with nullptr .

The core ingredient to map an enumeration (or an integer) to a type is full specialization of something. For example, you could have

template <TypeEnum> struct EnumToType;
template <> struct EnumToType<TypeA> { using type = TypeAType; };
template <> struct EnumToType<TypeB> { using type = TypeBType; };
// ...

... and then use that appropriately in your function, eg (this assumes that BaseType is actually a pointer type; if it is not, the values are sliced when creating the array and the concrete type cannot be recovered portably):

std::array<BaseType, 2> a = { TypeAInstance, TypeBInstance };
std::array<TypeEnum, 2> b = { TypeA, TypeB };

template<SearchTypeEnum E>
std::array<typename EnumToType<E>::type*, 2> GetEntriesOfType() {
    std::array<typename EnumToType<E>::type*, 2> rc;
    for (int i = 0; i != 2; ++i) {
        rc[i] = a[i] == E? static_cast<typename EnumToType<E>::type*>(b[i]);
    }
    return rc;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM