I'm trying to create a map that can work as such:
// I have A, B, and a templated class Processor
struct A;
struct B;
template<class T>
struct Processor {
Processor(T foo);
void SomeFunction();
};
// This is something like how I expect the map to be able to work
int main(){
A a;
B b;
TypeToValMap<KeyValue<A, Processor<A>>,
KeyValue<B, Processor<B>>> myMap{{a}, {b}}; // a and b will be passed to the constructors of the Processors
myMap.get<A>().SomeFunction(); // gets Processor<A> that was constructed with a
myMap.get<B>().SomeFunction(); // gets Processor<B> that was constructed with b
}
I was trying to implement it somewhere along the lines of
template<class Key, class Value>
struct KeyValue{
using key = Key;
using value = Value;
value val;
};
template<typename... Args>
struct TypeToValMap {
TypeToValMap(Args&&... args) {
//???
}
// This function should return the value by using T as the key
template<class T>
constexpr auto get(){ // ???? }
// Needs some container to hold the values?
};
I was trying to use something like
using Procs = std::variant<Processor<A>, Processor<B>>;
std::array<Procs, sizeof...(Args)>
or
std::array<std::any, sizeof...(Args)>
to hold the values, but couldn't get it working. Is it possible to implement such a thing?
Since you know all the types at compile time, you can store them in a container like std::tuple
.
If your mapping is always T
-> Processor<T>
, you can use a class to wrap the tuple like this:
template<class... T>
struct ProcessorMap : private std::tuple<Processor<T>...> {
private:
using tuple = std::tuple<Processor<T>...>;
public:
using tuple::tuple; // Use std::tuple constructors
template<class U>
decltype(auto) get() & {
return std::get<Processor<U>>(static_cast<tuple&>(*this));
}
template<class U>
decltype(auto) get() const& {
return std::get<Processor<U>>(static_cast<const tuple&>(*this));
}
template<class U>
decltype(auto) get() && {
return std::get<Processor<U>>(static_cast<tuple&&>(*this));
}
};
(Full example: https://godbolt.org/z/h7bK63qYh)
If your KeyValue
pairs are not so simple, you can convert a key to a value at compile time (Here, with lookup<Key, KeyValuePairs...>
)
template<class Key, class Value>
struct KeyValue {
using key = Key;
using value = Value;
};
template<class Key, class... Pairs>
struct lookup;
template<class Key, class Value, class... Pairs>
struct lookup<Key, KeyValue<Key, Value>, Pairs...> { using type = Value; };
template<class Key, class MismatchedKey, class Value, class... Pairs>
struct lookup<Key, KeyValue<MismatchedKey, Value>, Pairs...> : lookup<Key, Pairs...> {};
template<class Key> struct lookup<Key> {}; // Not found
template<class... Pairs>
struct ProcessorMap : private std::tuple<typename Pairs::value...> {
private:
using tuple = std::tuple<typename Pairs::value...>;
public:
using tuple::tuple; // Use std::tuple constructors
template<class U>
decltype(auto) get() & {
return std::get<typename lookup<U, Pairs...>::type>(static_cast<tuple&>(*this));
}
template<class U>
decltype(auto) get() const& {
return std::get<typename lookup<U, Pairs...>::type>(static_cast<const tuple&>(*this));
}
template<class U>
decltype(auto) get() && {
return std::get<typename lookup<U, Pairs...>::type>(static_cast<tuple&&>(*this));
}
};
Which is essentially the same, replacing Processor<U>
with lookup<U, Pairs...>::type
. Full example: https://godbolt.org/z/bc3KKcvj8