Skip to content

Instantly share code, notes, and snippets.

@talybin
Last active June 10, 2024 22:36
Show Gist options
  • Save talybin/c4856c217ad390eff98b24dbf7a000fb to your computer and use it in GitHub Desktop.
Save talybin/c4856c217ad390eff98b24dbf7a000fb to your computer and use it in GitHub Desktop.
Attempt to detect if function has variadic number of arguments
#include <utility>
struct any_type
{
template <class T>
constexpr operator T(); // non explicit
};
namespace detail
{
// from TS (https://en.cppreference.com/w/cpp/experimental/is_detected)
template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
struct detector
{
using value_t = std::false_type;
using type = Default;
};
template<class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...>
{
using value_t = std::true_type;
using type = Op<Args...>;
};
template<class Default, template<class...> class Op, class... Args>
using detected_or = detector<Default, void, Op, Args...>;
template< class Default, template<class...> class Op, class... Args >
using detected_or_t = typename detected_or<Default, Op, Args...>::type;
// --------------------------------
template <size_t N, class T>
using index_type = T;
template <class F, class>
struct function_ret_type;
template <class F, size_t... I>
struct function_ret_type<F, std::index_sequence<I...>>
{
// Return any_type if F is NOT callable with given number of arguments
using type = detected_or_t<
any_type,
std::invoke_result_t, F, index_type<I, any_type>...
>;
};
} // namespace detail
// Test if function is callable with sample number of arguments.
// Asuming that function has variadic number of arguments if all
// samples are callable.
template <class F>
struct is_variadic_arg_function {
private:
template <size_t N>
using fn_ret_t = typename
detail::function_ret_type<F, std::make_index_sequence<N>>::type;
template <class> struct test;
template <size_t... I>
struct test<std::index_sequence<I...>> {
// True if all samples are callable (has return type)
static constexpr bool value = (!std::is_same_v<any_type, fn_ret_t<2^I>> && ...);
};
public:
// Test 7 samples (1, 2, 4, 8, 16, 32 and 64 number of args)
static constexpr bool value = test<std::make_index_sequence<7>>::value;
};
template <class F>
constexpr inline bool is_variadic_arg_function_v = is_variadic_arg_function<F>::value;
@talybin
Copy link
Author

talybin commented Jun 9, 2024

Tests:

int main()
{
    // Variadic and return different types (harder to detect)
    auto var_fn = [](auto&&... args) {
        if constexpr (sizeof...(args) > 2)
            return std::string_view();
        else
            return 3.14f;
    };
    static_assert(is_variadic_arg_function_v<decltype(var_fn)>);

    auto arg1_fn = [](int, std::string, auto) {};
    static_assert(!is_variadic_arg_function_v<decltype(arg1_fn)>);

    auto arg2_fn = [](auto, auto) {};
    static_assert(!is_variadic_arg_function_v<decltype(arg2_fn)>);

    auto none_arg_fn = [] {};
    static_assert(!is_variadic_arg_function_v<decltype(none_arg_fn)>);

    // Optional args are very like variadic args,
    // more args harder to detect
    auto optional_args = [](int = 0, int = 0, int = 0) {};
    static_assert(!is_variadic_arg_function_v<decltype(optional_args)>);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment