I wrote a small utility function that will take two tuples and returns the Euclidean distance for the given indices only:
#include <cmath>
#include <tuple>
namespace {
template <typename T> constexpr auto sq(const T& val) { return val * val; }
}
template <int... I, typename... Tp>
constexpr auto euclidean_distance(const std::tuple<Tp...>& ta, const std::tuple<Tp...>& tb) {
return static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(
sqrt(
(
sq(
static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(std::get<I>(tb)) -
static_cast<std::common_type_t<std::tuple_element_t<I, std::decay_t<decltype(ta)>>...>>(std::get<I>(ta))
) + ...
)
)
);
}
constexpr std::tuple a { (int8_t)-10, -20, (uint16_t)50, "String", 1.2f };
constexpr std::tuple b { (int8_t)20, 120, (uint16_t)150, "Another", 9.1f };
int main() {
volatile auto res_i = euclidean_distance<0,1,2>(a, b);
volatile auto res_f = euclidean_distance<0,1,2,4>(a, b);
}
Right now it only takes tuples of completely equal type, but the template logic could be adapted to use tuples that only share similar types for the used indices. I'd love to hear any comments and critiques!
std::hypot()
avoid the issues of range and precision that a naivesqrt(sum(squares))
implementation suffers. It's a shame that it supports only three arguments, and not an arbitrary number, or you would then only need the element-selection logic and the arithmetic would be done. \$\endgroup\$