Skip to content

Instantly share code, notes, and snippets.

@jmal0
Last active April 13, 2018 20:43
Show Gist options
  • Save jmal0/38b2c0327b81bde3971639f0b328172f to your computer and use it in GitHub Desktop.
Save jmal0/38b2c0327b81bde3971639f0b328172f to your computer and use it in GitHub Desktop.
Generic metrics template metaprogram
#ifndef _GENERIC_METRICS_H
#define _GENERIC_METRICS_H
#include <cstddef>
#include <tuple>
#include <utility>
#if !__cpp_lib_tuples_by_type
static_assert(false, "Missing required C++14 feature");
#endif
namespace JMAL
{
//! @important Enum values must be consecutive and start at 0
enum class Metric
{
A = 0,
B,
C,
D,
num_metrics
};
//! @important Metric_calculator must be specialized for each unique Metric enum except num_metrics
template <typename T, Metric metric>
struct Metric_calculator
{
Metric_calculator() = delete;
template <typename... Args>
T operator()(Args&&... args) = delete;
};
template <>
struct Metric_calculator<int, Metric::A>
{
Metric_calculator() = default;
template <typename... Args>
int operator()(Args&&... args)
{
return 0;
}
};
template <>
struct Metric_calculator<int, Metric::B>
{
Metric_calculator() = default;
template <typename... Args>
int operator()(Args&&... args)
{
return 1;
}
};
template <>
struct Metric_calculator<int, Metric::C>
{
Metric_calculator() = default;
template <typename... Args>
int operator()(Args&&... args)
{
return 2;
}
};
template <>
struct Metric_calculator<int, Metric::D>
{
Metric_calculator() = default;
template <typename... Args>
int operator()(Args&&... args)
{
return val;
}
int val = 0;
};
// Forward declaration
template <typename T, Metric... metrics>
struct Metrics;
namespace detail
{
template <Metric... metrics>
struct Calculate_all_helper;
template <Metric metric, Metric... remaining_metrics>
struct Calculate_all_helper<metric, remaining_metrics...>
{
template <typename T,
Metric... all_metrics,
typename OutIter,
typename... Args>
static void calculate(Metrics<T, all_metrics...>& metrics_struct,
OutIter metrics_it,
Args&&... args)
{
*metrics_it = metrics_struct.template get<metric>()(std::forward<Args>(args)...);
Calculate_all_helper<remaining_metrics...>::calculate(metrics_struct,
++metrics_it,
std::forward<Args>(args)...);
}
};
template <>
struct Calculate_all_helper<>
{
template <typename T,
Metric... all_metrics,
typename OutIter,
typename... Args>
static void calculate(Metrics<T, all_metrics...>&,
OutIter,
Args&&...)
{
}
};
} // namespace detail
template <typename T, Metric... metrics>
struct Metrics
{
Metrics() = default;
template <typename OutIter, typename... Args>
void calculate_all(OutIter metrics_it,
Args&&... args)
{
using namespace detail;
Calculate_all_helper<metrics...>::calculate(*this,
metrics_it,
std::forward<Args>(args)...);
}
template <Metric metric>
Metric_calculator<T, metric>& get()
{
return std::get<Metric_calculator<T, metric>>(this->calculators);
}
template <Metric metric>
const Metric_calculator<T, metric>& get() const
{
return std::get<Metric_calculator<T, metric>>(this->calculators);
}
private:
std::tuple<Metric_calculator<T, metrics>...> calculators;
};
} // namespace JMAL
#endif
// Stupid.cpp
#include <numeric>
namespace JMAL
{
std::array<int, 4> f(Metrics<int, Metric::A, Metric::B, Metric::C, Metric::D>& metrics)
{
metrics.get<Metric::D>().val = 4;
std::array<int, 4> metric_vals;
metrics.calculate_all(metric_vals.begin());
return metric_vals;
}
int fsum(Metrics<int, Metric::A, Metric::B, Metric::C, Metric::D>& metrics)
{
auto metric_vals = f(metrics);
return std::accumulate(metric_vals.begin(), metric_vals.end(), 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment