есть ли std :: option_function, подобная данной - PullRequest
0 голосов
/ 06 января 2020

Я ищу что-то вроде стрижей? оператор в c ++ для std :: function. Мне нравилось это за последние пару лет.

Мне бы хотелось, чтобы std :: option_function вызывала функцию, только если функция существует.

Что-то вроде этого (но написано боги c ++):

template<typename R>
struct option_function_result {
    bool executed;
    R result;
} ;

template<>
struct option_function_result<void>
{
    bool executed;
} ;

template<typename F>
class optional_function
{
public:
    typedef std::function<F> function_type;
    typedef option_function_result<typename function_type::result_type> result_type;

protected:
    function_type f;

public:

    template<typename Fn>
    optional_function operator=(const Fn &f_)
    {
        f = f_;
        return *this;
    }

    template<typename Fn>
    optional_function operator=(Fn &&f_)
    {
        f = std::forward<Fn>(f_);
        return *this;
    }

    operator bool() const
    {
        return (bool)f;
    }

    template<typename ...Args, typename R>
    result_type operator()(Args... args)
    {
        if (f)
            return result_type { true, f(args...) };

        return result_type { false };
    }

    template<typename ...Args>
    result_type operator()(Args... args)
    {
        if (f)
        {
            f(args...);
            return result_type { true };
        }

        return result_type { false };
    }

} ;

Еще одна ревизия

Вот ревизия 2. Чтобы не задавать вопрос, и поскольку я не знаю, будет ли это окончательным ответом Я собираюсь разместить его здесь сейчас:

Я ожидаю, что конструктор для структуры не является необходимым. Однако это вынуждает компилятор выдавать мне ошибки, которые мне нужны для отладки компиляции.



template<typename R>
struct optional_function_result {
    bool executed;
    R result;

    optional_function_result(bool &&executed_, R &&result_) :
        executed (executed_),
        result(result_) {}
} ;

template<>
struct optional_function_result<void>
{
    bool executed;

    optional_function_result(bool &&executed_) :
        executed (executed_) {}
} ;

template<typename F>
class optional_function
{
public:
    typedef std::function<F> function_type;
    typedef typename std::function<F>::result_type function_result_type;
    typedef optional_function_result<typename function_type::result_type> result_type;

protected:
    function_type f;

public:

    template<typename Fn>
    optional_function operator=(const Fn &f_)
    {
        f = f_;
        return *this;
    }

    template<typename Fn>
    optional_function operator=(Fn &&f_)
    {
        f = std::forward<Fn>(f_);
        return *this;
    }

    operator bool() const
    {
        return (bool)f;
    }

    template<
        typename ... Args,
        typename FR=function_result_type,
        typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
    >
    result_type operator()(Args... args) const
    {
        if (f)
            return {
                true,
                std::forward<typename function_type::result_type>(f(args...))
            };

        return {
            false,
            function_result_type()
        };
    }

    template<
        typename ... Args,
        typename FR=function_result_type,
        typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
    >
    result_type operator()(Args... args) const
    {
        if (f)
        {
            f(args...);
            return { true };
        }

        return { false };
    }
} ;

Хорошо, еще одна версия, которая использует, в основном, опционально, чтобы избавиться от некоторых крайних случаев.

template<typename T>
using optional_type = std::experimental::optional<T>;

template<typename R>
struct optional_function_result : optional_type<R> {
    typedef optional_type<R> super_type;

    optional_function_result() :
        super_type() {}

    optional_function_result(R &&result_) :
        super_type(result_) {}

    bool executed() const { return this->has_result(); }
} ;

template<>
struct optional_function_result<void>
{
    bool executed_;

    optional_function_result(bool &&executed__) :
        executed_ (executed__) {}

    bool executed() const { return executed_; }
} ;

template<typename F>
class optional_function
{
public:
    typedef std::function<F> function_type;
    typedef typename std::function<F>::result_type function_result_type;
    typedef optional_function_result<typename function_type::result_type> result_type;

protected:
    function_type f;

public:

    template<typename Fn>
    optional_function operator=(const Fn &f_)
    {
        f = f_;
        return *this;
    }

    template<typename Fn>
    optional_function operator=(Fn &&f_)
    {
        f = std::forward<Fn>(f_);
        return *this;
    }

    operator bool() const
    {
        return (bool)f;
    }

    template<
        typename ... Args,
        typename FR=function_result_type,
        typename std::enable_if<!std::is_void<FR>::value, FR>::type* = nullptr
    >
    result_type operator()(Args... args) const
    {
        if (f)
            return {
                std::forward<typename function_type::result_type>(f(args...))
            };

        return {};
    }

    template<
        typename ... Args,
        typename FR=function_result_type,
        typename std::enable_if<std::is_void<FR>::value, FR>::type* = nullptr
    >
    result_type operator()(Args... args) const
    {
        if (f)
        {
            f(args...);
            return { true };
        }

        return { false };
    }
} ;

Ответы [ 3 ]

3 голосов
/ 06 января 2020

Оператор ? очень хорошо работает и в C ++:

// let function be of type std::function or a function pointer
auto var = f ? f() : default_value;

Если вам действительно нужен тип, который это делает, в стандартной библиотеке такого нет, но достаточно простой функции делать то, что вы хотите (работает только для функций, которые не возвращают ссылки или void):

template<typename F, typename... Args, typename R = std::invoke_result_t<F, Args&&...>>
auto optionally_call(F&& f, Args&&... args) -> std::optional<R> {
    return f ? R(std::forward<F>(f)(std::forward<Args>(args)...)) : std::nullopt;
}

С помощью некоторого метапрограммирования возможно поддерживать случаи, не поддерживаемые этой реализацией.

Это необходимо для того, чтобы подчеркнуть, что при создании целого типа, который должен быть обобщенным c, есть много подводных камней. Существует много ошибок, проблем с производительностью и даже кода, который не может быть вызван в вашем примере кода. Простая функция полезности будет проще, чем тип.

1 голос
/ 06 января 2020

Стандартная библиотека не имеет ничего подобного, но вы можете создать ее самостоятельно:

#include <functional>
#include <iostream>
#include <optional>

template <typename T>
class optional_function {
 private:
  std::optional<T> func;

 public:
  optional_function(T f) : func{std::move(f)} {}

  optional_function() = default;

  template <typename... Args>
  auto operator()(Args&&... args) const {
    using func_invoke_type = decltype((*func)(std::forward<Args>(args)...));

    constexpr bool func_invoke_type_is_void = std::is_same_v<void, func_invoke_type>;

    using optional_result_type = std::optional<
        std::conditional_t<
            func_invoke_type_is_void, // Can't have a std::optional<void>
            char,
            std::conditional_t<
                std::is_reference_v<func_invoke_type>, // Can't have a std::optional<T&>
                std::reference_wrapper<std::remove_reference_t<func_invoke_type>>,
                func_invoke_type
            >
        >
    >;

    if (func) {
      if constexpr (!func_invoke_type_is_void) {
        return optional_result_type{(*func)(std::forward<Args>(args)...)};
      } else {
        (*func)(std::forward<Args>(args)...);
        return optional_result_type{ '\0' }; //  can't return void{} '
      }
    }
    return optional_result_type{};
  }
};

// Test it

void foo() {}

int main() {
  optional_function f1{[](int i) { return i * i; }};
  optional_function f2{[] { std::cout << "Hello World\n"; }};
  decltype(f1) f3{};
  optional_function f4{[](int a, const int& b) -> int const& {
    std::cout << a + b << '\n';
    return b;
  }};

  optional_function f5{foo};

  auto res1 = f1(9);
  auto res2 = f2();
  auto res3 = f3(9);
  int b = 5;
  auto res4 = f4(1, b);
  auto res5 = f5();

  std::cout << std::boolalpha;
  std::cout << "f1 is executed: " << res1.has_value() << ". result: " << *res1
            << '\n';
  std::cout << "f2 is executed: " << res2.has_value() << '\n';
  std::cout << "f3 is executed: " << res3.has_value() << '\n';
  std::cout << "f4 is executed: " << res4.has_value() << ". result: " << *res4
            << '\n';
  std::cout << "f5 is executed: " << res5.has_value() << '\n';
}

0 голосов
/ 06 января 2020

Нет, в настоящее время в стандартной библиотеке C ++ такого нет.

...