JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
lookup_map.h File Reference

constexpr and runtime fixed-size hash lookup table. More...

#include <array>
#include <algorithm>
#include <type_traits>
#include <utility>
#include <concepts>
#include <string>
#include <string_view>
#include <cstddef>
#include <cstdint>
#include "jh/pods/array.h"
#include "jh/pods/string_view.h"
#include "jh/core/immutable_str.h"
#include "jh/conceptual/hashable.h"
#include "jh/metax/t_str.h"

Go to the source code of this file.

Classes

struct  jh::meta::extension::key_traits< T >
 Default key conversion traits. More...
struct  jh::meta::extension::key_traits< jh::pod::string_view >
 Key traits specialization for jh::pod::string_view. More...
struct  jh::meta::lookup_map< K, V, N, Hash >
 Fixed-capacity hash-based flat map providing switch-like lookup semantics. More...
struct  jh::meta::lookup_map< K, V, N, Hash >::entry
 Single entry stored in the lookup table. More...

Namespaces

namespace  jh::meta
 Aggregated entry point for compile-time metaprogramming utilities.
namespace  jh::meta::extension
 Public customization points for compile-time key transformation and heterogeneous lookup in jh::meta.

Concepts

concept  jh::meta::transparent_key
 Concept checking whether key conversion through key_traits<K> is valid.

Functions

template<typename K, typename V, std::size_t N>
 jh::meta::lookup_map (std::array< std::pair< K, V >, N > &&) -> lookup_map< K, V, N, jh::hash< K > >
 Deduction guide for constructing lookup_map from an array of pairs.
template<typename K, typename V, std::size_t N>
 jh::meta::lookup_map (std::array< std::pair< K, V >, N > &&, V) -> lookup_map< K, V, N, jh::hash< K > >
 Deduction guide for constructing lookup_map with an explicit default value.
template<typename Hash, typename K, typename V, std::size_t N>
 jh::meta::lookup_map (std::array< std::pair< K, V >, N > &&, V, Hash) -> lookup_map< K, V, N, Hash >
 Deduction guide for constructing lookup_map using a user-provided hash.
template<typename Hash, typename K, typename V, std::size_t N>
consteval auto jh::meta::make_lookup_map (const std::array< std::pair< K, V >, N > &init, V default_value=V{}, Hash hash_fn=Hash{})
 Compile-time constructor for lookup_map with explicit hash.
template<typename K, typename V, std::size_t N>
consteval auto jh::meta::make_lookup_map (const std::array< std::pair< K, V >, N > &init, V default_value=V{})
 Compile-time constructor using automatically deduced jh::hash<K>.
template<typename V, std::size_t N>
consteval auto jh::meta::make_lookup_map (const std::array< std::pair< std::string_view, V >, N > &init, V default_value=V{})
 Compile-time constructor for tables declared with std::string_view keys.

Detailed Description

constexpr and runtime fixed-size hash lookup table.

Author
JeongHan-Bae <mastropseudo@gmail.com>

Overview

Provides a sorted flat-map based on precomputed hash values. The table has fixed capacity N, no allocation, deterministic layout, and allows transparent key conversion. Lookup complexity is logarithmic by hash and linear only within equal-hash ranges.

Features

  • constexpr construction
  • transparent key transformation
  • binary search on sorted hashes
  • POD optimization into read-only memory

Eliminating hash-switch boilerplate

A common pattern in large systems is manual hash-dispatch:

auto h = hash(input);
switch (h) {
case HASH_A:
if (input == A) { ... }
break;
case HASH_B:
if (input == B) { ... }
break;
}

This pattern duplicates hashing logic, requires manual collision guards, scatters string or object comparisons across branches, and grows linearly in maintenance cost as the number of cases increases.

lookup_map removes this entire category of boilerplate. The table stores precomputed hashes and performs structured binary search followed by equality verification. The user never writes manual hash constants or collision checks.

Recommended architectural pattern

The container is intended to enforce a disciplined flow:

  1. Normalize external input into a lightweight View type.
  2. Use lookup_map with<TypeView, CommandEnum> for mapping.
  3. Dispatch using CommandEnum in a centralized switch.

For complex input types, define a corresponding TypeView. The View should:

  • Be lightweight and non-owning.
  • Represent only the identifying portion of the object.
  • Be hashable via jh::hash<TypeView>.
    (The deduction is std::hash<T>{}(t) > ADL hash(t) > no-param t.hash(), at least one should be provided for the View type.)

Then provide normalization through the extension layer:

struct TypeView { ... };
// operator== and hash support for TypeView
template<>
struct key_traits<TypeView> {
using canonical_type = TypeView;
using apparent_type = const Type &;
static constexpr TypeView
to_canonical(const Type & obj) noexcept {
return make_view(obj);
}
};

After normalization, the mapping becomes purely structural:

constexpr auto table =
make_lookup_map<HashMethod>(
std:array{
std::pair{TypeView{...}, CommandEnum::A},
std::pair{TypeView{...}, CommandEnum::B},
...
},
CommandEnum::UNKNOWN // Default
);
Definition flatten_proxy.h:180

If jh::hash<TypeView> can be deduced and is constexpr-safe, omit the HashMethod template argument:

constexpr auto table =
make_lookup_map(
std:array{
std::pair{TypeView{...}, CommandEnum::A},
std::pair{TypeView{...}, CommandEnum::B},
...
},
CommandEnum::UNKNOWN // Default
);

Finally,

CommandEnum cmd = table[input_object];
switch (cmd) {
case CommandEnum::A:
handle_A(...);
break;
case CommandEnum::B:
handle_B(...);
break;
...
case CommandEnum::UNKNOWN:
default:
handle_unknown(...);
break;
}

Philosophy

The goal is not to provide a generic container, but to formalize a deterministic dispatch pipeline:

External input
    ->
View normalization
    ->
Static mapping to CommandEnum
    ->
Centralized switch dispatch

This separates parsing from behavior, removes repetitive hash-switch patterns, reduces maintenance overhead, and encourages strongly typed dispatch instead of string-driven branching.