Giter VIP home page Giter VIP logo

void's Introduction

Regular Void: The Library

Tests

P0146 describes the issue at hand pretty well: in generic code, we often want to deal with functions that can return any type - and we don't actually care what that type is. Example from the paper:

// Invoke a Callable, logging its arguments and return value.
// Requires an exact match of Callable&&'s pseudo function type and R(P...).
template<class R, class... P, class Callable>
R invoke_and_log(callable_log<R(P...)>& log, Callable&& callable,
                 std::add_rvalue_reference_t<P>... args) {
  log.log_arguments(args...);
  R result = std::invoke(std::forward<Callable>(callable),
                         std::forward<P>(args)...);
  log.log_result(result);
  return result;
}

This is correct for all R except void. For void, specifically, we need dedicated handling:

if constexpr (std::is_void_v<R>) {
    std::invoke(std::forward<Callable>(callable),
                std::forward<P>(args)...);
    log.log_result();
} else {
    R result = std::invoke(std::forward<Callable>(callable),
                           std::forward<P>(args)...);
    log.log_result(result);
    return result;
}

This is tedious and error-prone. While P0146 proposed a language solution to this problem (letting the original code just work with R=void), the paper also pointed out that this could be helped with a library-based solution.

This is such a (C++17) library-based solution.

It features the following:

  • a type, vd::Void, that is a regular unit type.

  • metafunctions vd::wrap_void and vd::unwrap_void that convert void to vd::Void and back.

  • a function vd::invoke that is similar to std::invoke except that:

    • it returns vd::Void instead of void, where appropriate, and
    • vd::invoke(f, vd::Void{}) is equivalent to vd::invoke(f) (regardless of whether f is invocable with vd::Void). See rationale.
  • a metafunction vd::void_result_t that is to vd::invoke what std::invoke_result_t is to std::invoke, except that it still gives you void (instead of Void). See rationale.

  • (on C++20) a concept vd::invocable that is to vd::invoke what std::invocable is to std::invoke

This library allows the above code to be handled as:

template<class R, class... P, class Callable>
vd::wrap_void<R> invoke_and_log(
                 callable_log<R(P...)>& log, Callable&& callable,
                 std::add_rvalue_reference_t<P>... args) {
  log.log_arguments(args...);
  vd::wrap_void<R> result = vd::invoke(VD_FWD(callable), VD_FWD(args)...);
  // either pass result in directly, if passing Void is acceptable
  log.log_result(result);
  // Or use vd::invoke again to be able to call log.log_result()
  // in the void case.
  // VD_LIFT is a macro that turns a name into a function object.
  vd:invoke([&] VD_LIFT(log.log_result), result);
  return result;
}

Design Rationale

There is basically only one interesting design choice in this library (the rest kind just falls out of wanting to solve the problem) and that is having vd::invoke(f, vd::Void{}) fallback to trying f() if f(vd::Void{}) is not a valid expression.

The reason for this (along with the corresponding unwrap in vd::void_result_t) is that it ends up being more useful for common use-cases. Such as:

template <class T>
class Optional {
    union { vd::wrap_void<T> value_; };
    bool engaged_;

public:
    template <class F>
    auto map(F f) const -> Optional<vd::void_result_t<F&, vd::wrap_void<T> const&>>
    {
        if (engaged_) {
            return vd::invoke(f, value_);
        } else {
            return {};
        }
    }
};

For Optional<void> (a seemingly pointless, yet nevertheless useful specialization), this allows map to take a nullary function. And for Optional<T>, if F returns void, we get back Optional<void>. Everything works out quite nicely.

void's People

Contributors

brevzin avatar

Stargazers

GAURAV avatar Igor Zarzycki avatar Jia Yue Hua avatar  avatar Krayfaus avatar Salim Pamukcu avatar  avatar Kobi Cohen-Arazi avatar Cycatz avatar Sergei Bastrakov avatar

Watchers

 avatar

Forkers

icodein

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.