#pragma once #include #include namespace kpn { // ── Primary template — not defined; only specialisations match ──────────────── template struct function_traits; // Free function template struct function_traits { using return_t = R; using args = std::tuple; static constexpr std::size_t arity = sizeof...(Args); }; // Function pointer template struct function_traits : function_traits {}; // Member function pointer (const) template struct function_traits : function_traits {}; // Member function pointer (non-const) template struct function_traits : function_traits {}; // Callable (lambda / std::function) — delegate to operator() template struct function_traits : function_traits {}; // ── Helpers ─────────────────────────────────────────────────────────────────── template using return_t = typename function_traits>::return_t; template using args_t = typename function_traits>::args; template inline constexpr std::size_t arity_v = function_traits>::arity; // ── Tuple detection ─────────────────────────────────────────────────────────── template struct is_tuple : std::false_type {}; template struct is_tuple> : std::true_type {}; template inline constexpr bool is_tuple_v = is_tuple::value; // ── Normalise return type to always be a tuple ──────────────────────────────── // void → std::tuple<> // T (non-tup) → std::tuple // tuple<...> → tuple<...> (unchanged) template struct normalise_return { using type = std::tuple; }; template<> struct normalise_return { using type = std::tuple<>; }; template struct normalise_return> { using type = std::tuple; }; template using normalised_return_t = typename normalise_return::type; // ── Output count from a function type ──────────────────────────────────────── template inline constexpr std::size_t output_count_v = std::tuple_size_v>>; } // namespace kpn