Skip to content

Latest commit

 

History

History
200 lines (153 loc) · 7.46 KB

187.md

File metadata and controls

200 lines (153 loc) · 7.46 KB
Info

Example

int main() {
  void (*f1)();
  void (*f2)() noexcept;

  f1 = f2; // okay
  f2 = f1; // ill-formed since C++17
}

https://godbolt.org/z/Majq8f

Puzzle

  • Can you implement function_args_t type trait which takes an invocable, respect noexcept and returns its arguments?
struct args{};
struct va_args {};

template<class T>
using function_args_t = /*TODO*/

int f1();
int f2() noexcept;
constexpr auto f3() noexcept -> void;
constexpr auto f4(...) noexcept -> void;
constexpr auto f5(int, ...) noexcept -> void;
constexpr auto f6(int...) noexcept -> void;

auto l1 = [] {};
auto l2 = [](int...) noexcept {};
auto l3 = [](auto...) noexcept {};
auto l4 = [](auto......) noexcept {};
constexpr auto l5 = [](auto..., auto......) constexpr noexcept {};
constexpr auto l6 = []<class... Ts>(Ts..., auto......) constexpr noexcept {};
constexpr auto l7 = []<auto...>(...) constexpr noexcept {};

struct f {
  constexpr auto operator()(int, double, float) noexcept(false) -> void;
};

static_assert(std::is_same_v<std::tuple<>, function_args_t<decltype(&f1)>>);
static_assert(std::is_same_v<std::tuple<>, function_args_t<decltype(&f2)>>);
static_assert(std::is_same_v<std::tuple<>, function_args_t<decltype(&f3)>>);
static_assert(std::is_same_v<std::tuple<va_args>, function_args_t<decltype(&f4)>>);
static_assert(std::is_same_v<std::tuple<int, va_args>, function_args_t<decltype(&f5)>>);
static_assert(std::is_same_v<std::tuple<int, va_args>, function_args_t<decltype(&f6)>>);

static_assert(std::is_same_v<std::tuple<>, function_args_t<decltype(l1)>>);
static_assert(std::is_same_v<std::tuple<int, va_args>, function_args_t<decltype(l2)>>);
static_assert(std::is_same_v<std::tuple<args>, function_args_t<decltype(l3)>>);
static_assert(std::is_same_v<std::tuple<args, va_args>, function_args_t<decltype(l4)>>);
static_assert(std::is_same_v<std::tuple<args, va_args>, function_args_t<decltype(l5)>>);
static_assert(std::is_same_v<std::tuple<args, va_args>, function_args_t<decltype(l6)>>);
static_assert(std::is_same_v<std::tuple<va_args>, function_args_t<decltype(l7)>>);

static_assert(std::is_same_v<std::tuple<int, double, float>, function_args_t<f>>);

https://godbolt.org/z/Gq1Pxe

Solutions

// forward decl for later specialization
template <class T> struct function_args;

// specialization for regular function
template <class R, class... Ts, auto NE>
struct function_args<R(Ts...) noexcept(NE)> {
  using arg_list_t = std::tuple<Ts...>;
};

// specialization for varargs function
template <class R, class... Ts, auto NE>
struct function_args<R(Ts..., ...) noexcept(NE)> {
  using arg_list_t = std::tuple<Ts..., va_args>;
};

// combinatorial explosion ahead

// specializations for const and non-const member functions
// with and without varargs
template <class C, class R, class... Ts, auto NE>
struct function_args<R(C::*)(Ts...) const noexcept(NE)> : function_args<R(Ts...) noexcept(NE)> {};

template <class C, class R, class... Ts, auto NE>
struct function_args<R(C::*)(Ts..., ...) const noexcept(NE)> : function_args<R(Ts..., ...) noexcept(NE)> {};

template <class C, class R, class... Ts, auto NE>
struct function_args<R(C::*)(Ts...) noexcept(NE)> : function_args<R(Ts...) noexcept(NE)> {};

// technically not needed for test
//template <class C, class R, class... Ts, auto NE>
//struct function_args<R(C::*)(Ts..., ...) noexcept(NE)> : function_args<R(Ts..., ...) noexcept(NE)> {};

// function overloads for getting the right thing out
// raw function pointers
template <class T> requires std::is_pointer_v<T>
constexpr auto fn_args() -> function_args<std::remove_pointer_t<T>>;

// non-generic lambdas
template <class T> requires requires { &T::operator(); }
constexpr auto fn_args() -> function_args<decltype(&T::operator())>;

// generic lambdas with class template parms
template <typename T> requires requires { &T::template operator()<args>; }
constexpr auto fn_args() -> function_args<decltype(&T::template operator()<args>)>;

// generic lambdas with NTTPs
template <typename T> requires requires { &T::template operator()<1>; }
constexpr auto fn_args() -> function_args<decltype(&T::template operator()<1>)>;

// one alias to rule them all
template <class T>
using function_args_t = decltype(fn_args<T>())::arg_list_t;

https://godbolt.org/z/s7h3Tn

template<class T>
struct function_traits : function_traits<decltype(&T::operator())> {};

template<class T> requires requires { T::template operator()<args>; }
struct function_traits<T> : function_traits<decltype(&T::template operator()<args>)> {};

template<class T> requires requires { T::template operator()<0>; }
struct function_traits<T> : function_traits<decltype(&T::template operator()<0>)> {};

template<class R, class... TArgs, auto Noexcept>
struct function_traits<R(*)(TArgs...) noexcept(Noexcept)> { using args = std::tuple<TArgs...>; };

template<class R, class B, class... TArgs>
struct function_traits<R(B::*)(TArgs...)> { using args = std::tuple<TArgs...>; };

template<class R, class... TArgs, auto Noexcept>
struct function_traits<R(*)(TArgs......) noexcept(Noexcept)> { using args = std::tuple<TArgs..., va_args>; };

template<class R, class B, class... TArgs, auto Noexcept>
struct function_traits<R(B::*)(TArgs...) const noexcept(Noexcept)> { using args = std::tuple<TArgs...>; };

template<class R, class B, class... TArgs, auto Noexcept>
struct function_traits<R(B::*)(TArgs......) const noexcept(Noexcept)> { using args = std::tuple<TArgs..., va_args>; };

template<class T>
using function_args_t = typename function_traits<T>::args;

https://godbolt.org/z/9zcMe5

namespace detail {
    constexpr int some_nttp = 0;
}

template<typename T> struct func_info_t;
template<typename Ret, typename... Args, auto no_except_val>
struct func_info_t<Ret(*)(Args...) noexcept(no_except_val)> {
    using args_t = std::tuple<Args...>;
};
template<typename Ret, typename... Args, auto no_except_val>
struct func_info_t<Ret(*)(Args..., ...) noexcept(no_except_val)> {
    using args_t = std::tuple<Args..., va_args>;
};
template<typename Class, typename Ret, typename... Args, auto no_except_val>
struct func_info_t<Ret(Class::*)(Args...) noexcept(no_except_val)> {
    using args_t = std::tuple<Args...>;
};
template<typename Class, typename Ret, typename... Args, auto no_except_val>
struct func_info_t<Ret(Class::*)(Args...) const noexcept(no_except_val)> {
    using args_t = std::tuple<Args...>;
};
template<typename Class, typename Ret, typename... Args, auto no_except_val>
struct func_info_t<Ret(Class::*)(Args..., ...) const noexcept(no_except_val)> {
    using args_t = std::tuple<Args..., va_args>;
};

template<typename T> requires std::is_pointer_v<T>
constexpr auto function_pointer_maker() -> T;
template<typename T> requires requires { &T::operator(); }
constexpr auto function_pointer_maker() -> decltype(&T::operator());
template<typename T> requires requires { &T::template operator()<args>; }
constexpr auto function_pointer_maker() -> decltype(&T::template operator()<args>);
template<typename T> requires requires { &T::template operator()<detail::some_nttp>; }
constexpr auto function_pointer_maker() -> decltype(&T::template operator()<detail::some_nttp>);

https://godbolt.org/z/d9YxP9