Skip to content

Latest commit

 

History

History
211 lines (169 loc) · 5.16 KB

305.md

File metadata and controls

211 lines (169 loc) · 5.16 KB
Info

Example

#include <concepts>

// auto fn(int...) -> void;   // error
auto fn(int, ...) -> void ;   // ok
auto fn(std::same_as<int> auto...) -> void; // ok

https://godbolt.org/z/cfq78aWj5

Puzzle

  • Can you implement safe_call which will call given function with variadic type-safe input parameters from propagted from ...?

    • Max number of parameters is 3
    • fn is expected to be called with types coresponding to input parameters (not just strings, for example)
auto safe_call(auto fn, auto fmt, ...);  // TODO

int main() {
    {
    std::stringstream str{};
    safe_call([&](auto... args) { ((str << args), ...); }, "");
    assert(std::string{""} == str.str());
    }

    {
    std::stringstream str{};
    safe_call([&](auto... args) { ((str << args), ...); }, "id", 4, 2.);
    assert(std::string{"42"} == str.str());
    }

    {
    std::stringstream str{};
    safe_call([&](auto... args) { ((str << args), ...); }, "di", 3.2, 1);
    assert(std::string{"3.21"} == str.str());
    }

    {
    std::stringstream str{};
    safe_call([&](auto... args) { ((str << args), ...); }, "idi", 1, 2.3, 4);
    assert(std::string{"12.34"} == str.str());
    }
}

https://godbolt.org/z/eT7vnM13E

Solutions

void assert_fmt(char fmt, char required_fmt) {
    if (fmt != required_fmt) {
        throw std::runtime_error(std::string("fmt must be '") + required_fmt + "'");
    }
}

template<class T>
requires std::same_as<T, int>
void assert_fmt(char fmt) {
    assert_fmt(fmt, 'i');
}

template<class T>
requires std::same_as<T, double>
void assert_fmt(char fmt) {
    assert_fmt(fmt, 'd');
}

template<class T>
auto safe_call_helper(auto fn, auto fmt, std::same_as<T> auto arg) {
    assert_fmt<T>(*fmt);
    fn(arg);
}

template<class T, class U>
auto safe_call_helper(auto fn, auto fmt, T arg_0, U arg_1, auto ... args) {
    assert_fmt<T>(*fmt);
    fn(arg_0);
    safe_call_helper<U>(fn, fmt + 1, arg_1, args...);
}

template<class ... Ts>
using FirstType = std::tuple_element_t<0, std::tuple<Ts...>>;

template<class ... Ts>
auto safe_call(auto fn, auto fmt, Ts ... args)  {
    if constexpr (sizeof...(Ts)) {
        safe_call_helper<FirstType<Ts...>>(fn, fmt, args...);
    }

https://godbolt.org/z/bhPxaf7Ga

template <auto N>
auto safe_call_impl(auto fn, auto fmt, va_list args, auto... ts) {
    if constexpr (N == 0) {
        fn(ts...);
    } else {
        if (*fmt) {
            switch (*fmt) {
                case 'i':
                    return safe_call_impl<N - 1>(fn, fmt + 1, args, ts...,
                                                 va_arg(args, int));
                case 'd':
                    return safe_call_impl<N - 1>(fn, fmt + 1, args, ts...,
                                                 va_arg(args, double));
            }
        } else {
            fn(ts...);
        }
    }
}

auto safe_call(auto fn, auto fmt, ...) {
    std::va_list args{};
    va_start(args, fmt);
    safe_call_impl<3>(fn, fmt, args);
    va_end(args);

https://godbolt.org/z/8bs8qe4MG

auto safe_call(auto fn, std::string_view fmt, ...) -> decltype(auto) {
  std::va_list args;
  va_start(args, fmt);

  using namespace boost::mp11;

  return mp_with_index<4>(
      std::size(fmt), [&]<auto N>(mp_size_t<N>) -> decltype(auto) {
        mp_repeat_c<std::tuple<std::variant<int, double>>, N> t;

        mp_for_each<mp_iota_c<N>>([&]<auto I>(mp_size_t<I>) {
          switch (fmt[I]) {
            case 'i':
              std::get<I>(t) = va_arg(args, int);
              break;
            case 'd':
              std::get<I>(t) = va_arg(args, double);
              break;
          }
        });

        va_end(args);

        return std::apply(
            [&](const auto &...vars) -> decltype(auto) {
              return std::visit(fn, vars...);
            },
            t);
      });
}

https://godbolt.org/z/bvWq9vEna

auto safe_call(auto fn, auto fmt, ...) {
    std::va_list args;
    va_start(args, fmt);
    std::stringstream sout;
    while(*fmt != '\0') {
        if (*fmt=='d')
            fn(va_arg(args, double));
        if (*fmt =='i')
            fn(va_arg(args, int));
        fmt++;
    }
    va_end(args);
}

https://godbolt.org/z/TbYrEcPfG

auto safe_call(auto fn, auto fmt, auto... args){
    
    auto safe_check = [&]<std::size_t... Ns>(std::index_sequence<Ns...>){
        auto type_check = [](char type, auto arg){
            if( type == 'i'){
                return std::is_convertible_v<decltype(arg),int>;
            }else if( type == 'd'){
                return std::is_convertible_v<decltype(arg),float>;
            }
            return false;
        };
        return (true && ... && type_check(fmt[Ns],args));
    }(std::make_index_sequence<sizeof...(args)>{});

    if(safe_check)
    {
        return fn(args...);
    }
}

https://godbolt.org/z/5foWccqee