Skip to content

Instantly share code, notes, and snippets.

@dodheim
Created August 28, 2017 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dodheim/d838ea0ff6e3cbd9a494e45c6c3f59f4 to your computer and use it in GitHub Desktop.
Save dodheim/d838ea0ff6e3cbd9a494e45c6c3f59f4 to your computer and use it in GitHub Desktop.
Boost.Hana fusion_map demo for /u/Bitter_Peter
#define BOOST_HANA_CONFIG_ENABLE_STRING_UDL 1
#include <type_traits>
#include <utility>
#include <optional>
#include <string_view>
#include <array>
#include <range/v3/core.hpp>
#include <range/v3/algorithm/lower_bound.hpp>
#include <boost/hana.hpp>
#include <boost/hana/ext/std/integer_sequence.hpp>
#include <boost/hana/ext/std/pair.hpp>
namespace hana = boost::hana;
using namespace hana::literals;
// named type to hold desired registry values for expositional purposes
struct pressure_value {
explicit constexpr pressure_value(int const val) noexcept : value(val) { }
int value;
};
using pressure = std::pair<std::string_view, pressure_value>;
// registry-entry types; it must be possible to derive a pressure_value from each
struct foo { pressure_value pressure_val{1}; /*other state*/ };
struct bar { pressure_value pressure_val{2}; /*other state*/ };
struct baz { constexpr auto pressure_val() const { return pressure_value{3}; } /*other state*/ };
struct qux { constexpr int get_pressure_val() const { return 4; } /*other state*/ };
// compile-time registry
auto constexpr registry = hana::make_map(
hana::make_pair("foo"_s, foo{}),
hana::make_pair("bar"_s, bar{}),
hana::make_pair("baz"_s, baz{}),
hana::make_pair("qux"_s, qux{})
);
namespace detail {
// customization point in case registry-entry types do not expose their desired value uniformly;
// unnecessary if they do
auto constexpr extract_pressure_val = hana::overload(
// default case
[](auto const& re) { return re.pressure_val; },
// specialized cases
[](baz const& b) { return b.pressure_val(); },
[](qux const& q) { return pressure_value{q.get_pressure_val()}; }
);
// converts hana::pair<StringT, RegistryEntryT> to std::pair<string_view, pressure_value>
auto constexpr make_pressure = [](auto const n, auto const& re) -> pressure {
return {{n.c_str(), hana::length(n)}, detail::extract_pressure_val(re)};
};
}
// runtime registry; (constexpr) std::array for this demo, but could be any container type e.g. unordered_map
auto constexpr pressure_registry = hana::unpack(
std::make_index_sequence<hana::length(registry)>{},
[](auto const... is) -> std::array<pressure, sizeof...(is)> {
auto constexpr ns = hana::sort(hana::to_tuple(hana::keys(registry)));
return {{detail::make_pressure(ns[is], registry[ns[is]])...}};
}
);
auto constexpr lookup_pressure_val = hana::overload(
// compile-time key lookup
[](auto const n) -> std::enable_if_t<hana::is_a<hana::string_tag, decltype(n)>(), std::optional<pressure_value>> {
if constexpr(auto re = hana::find(registry, n); hana::is_just(re)) {
return {detail::extract_pressure_val(*re)};
} else {
return {};
}
},
// runtime key lookup
[](std::string_view const n) {
std::optional<pressure_value> ret;
// using lower_bound since we have a sorted array; use appropriate logic depending
// on the actual type of pressure_registry e.g. member .find() if it is an unordered_map
if (auto const it = ranges::lower_bound(pressure_registry, n, ranges::ordered_less{}, hana::first);
it != ranges::end(pressure_registry) && it->first == n)
{
ret = it->second;
}
return ret;
}
);
////////////////////////////////////////////////////////////////////////////////
// demo
#include <cstdlib>
#include <exception>
#include <string>
#include <iomanip>
#include <iostream>
#include <boost/hana/experimental/printable.hpp>
using namespace std::string_view_literals;
std::ostream& operator <<(std::ostream& os, foo const& f) {
return os << "foo{"sv << f.pressure_val.value << "}\n"sv;
}
std::ostream& operator <<(std::ostream& os, bar const& b) {
return os << "bar{"sv << b.pressure_val.value << "}\n"sv;
}
std::ostream& operator <<(std::ostream& os, baz const& b) {
return os << "baz{"sv << b.pressure_val().value << "}\n"sv;
}
std::ostream& operator <<(std::ostream& os, qux const& q) {
return os << "qux{"sv << q.get_pressure_val() << "}\n"sv;
}
static void demo() {
using namespace std::string_literals;
std::cout << "registry:\n"sv << hana::experimental::print(registry) << '\n';
std::cout << "\npressure_registry: \n"sv;
for (auto const [n, pv] : pressure_registry) {
std::cout << '\t' << std::quoted(n) << " :: "sv << pv.value << '\n';
}
std::cout << "\nlookup results: \n"sv;
static auto constexpr print_lookup_results = [](auto const n) {
std::cout << '\t';
if constexpr(hana::is_a<hana::string_tag, decltype(n)>()) {
std::cout << std::quoted(n.c_str()) << " (compile-time lookup)"sv;
} else {
std::cout << std::quoted(n) << " (runtime lookup)"sv;
}
std::cout << " :: "sv;
if (auto const pv = lookup_pressure_val(n)) {
std::cout << pv->value;
} else {
std::cout << "{nullopt}"sv;
}
std::cout << '\n';
};
print_lookup_results("qux"_s); // pass (hana::string)
print_lookup_results("baz"sv); // pass (std::string_view)
print_lookup_results("bar"s); // pass (std::string)
print_lookup_results("foo"); // pass (char const*)
print_lookup_results("corge"sv); // fail (std::string_view)
print_lookup_results("corge"_s); // fail (hana::string)
}
int main() {
std::ios::sync_with_stdio(false);
std::cout << std::boolalpha;
std::cerr << std::boolalpha;
try {
demo();
return EXIT_SUCCESS;
} catch (std::exception const& ex) {
std::cerr << ex.what() << '\n';
} catch (...) {
std::cerr << "unknown exception (...)\n"sv;
}
return EXIT_FAILURE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment