Last active
April 13, 2018 20:43
-
-
Save jmal0/38b2c0327b81bde3971639f0b328172f to your computer and use it in GitHub Desktop.
Generic metrics template metaprogram
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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