Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking a map statically at compile time?

So, I'll preface this question with that I am not sure if this is even possible.

Anyways, I am attempting to have a static const map (or something similar) checked at compile time to verify it will have a matching value (an id in this case). Specifically, I currently have something bad like this (but it doesn't compile):

template<typename T>
struct TypeMapChecker
{
    static const std::map<std::type_info, MessageID> type_map;
    static const bool contains = (type_map.count(typeid(T)) > 0);
}

template<typename T> 
const std::map<std::type_info, MessageID> TypeMapChecker<T>::type_map = {
    { typeid(ClassA), MESSAGE_A_ID }, 
    { typeid(ClassB), MESSAGE_B_ID }
};

..........

template<typename T0, typename T1>
class MessagePair
{
    static const bool t0Has = TypeMapChecker<T0>::contains;
    static const bool t1Has = TypeMapChecker<T1>::contains;
    static_assert (t0Has && t1Has, "I'M ASSERTING MY AUTHORITY, YOU MALIGNANT CODERS");
}

Something like that. This doesn't build because "expected a constant expression" at:

static const bool contains = (type_map.count(typeid(T)) > 0);

Just want to validate that when I access the type_map in MessagePair I am guaranteed a value at build time, and don't have to rely on runtime shenanigans since this is a large code base modified by many (and I can't stop them from doing bad things at runtime). Other implementations or any help would be greatly appreciated. Thanks.

like image 484
mascoj Avatar asked Oct 20 '25 02:10

mascoj


1 Answers

Store the map as a type.

Use the type to build your runtime typeid map.

Here is the key-value pair for your compile-time map:

template<class Key, class Value>
struct entry {
  using key=Key;
  using value=Value;
};

We then make a map from it:

template<class T> struct tag_t{using type=T; constexpr tag_t(){};};
template<class T> constexpr tag_t<T> tag{};

template<class...Entries>
struct compile_time_map:Entries... {
  template<class Key>
  struct lookup {
    template<class Value>
    constexpr tag_t<Value> operator()( entry<Key, Value> const& ) const { return {}; }
  };
  template<class Key>
  constexpr
  std::result_of_t< lookup<Key>( compile_time_map ) > operator()( tag_t<Key> ) const {
    return {};
  }
  template<class...Key>
  constexpr std::false_type operator()( tag_t<Key...> ) const {
    return {};
  }
  template<class MessageID>
  std::map< std::type_index, MessageID > make_map() const {
    return { {typeid(typename Entries::key), typename Entries::value{}}... };
  }

  template<class Key>
  constexpr auto has(tag_t<Key> ={}) const {
    return std::integral_constant<bool, !std::is_same< std::result_of_t< compile_time_map(tag_t<Key>)>, std::false_type >{}>{};
  }
};

This map can generate a run-time map of a type of your choice.

using ID=unsigned;

template<class T, ID id>
using make_entry = entry<T, std::integral_constant<ID, id>>;

using ctm = compile_time_map<
  make_entry< int, 7 >,
  make_entry< double, 3 >,
  make_entry< std::string, (unsigned)-1 >
>;

auto rtm = ctm{}.make_map<ID>();

we can do compile-time lookups with ctm{}( tag<int> ). We can do runtime lookups with rtm[ typeid(int) ].

We can check if there is an entry at compile time with ctm{}.has<int>() or ctm{}.has( tag<int> ).

live example.

like image 101
Yakk - Adam Nevraumont Avatar answered Oct 21 '25 17:10

Yakk - Adam Nevraumont