0

Is there any "tool" in standard, that for such template function:

template<typename FirstArg, typename... Args>
auto average(FirstArg &&firstArg_, Args&&... args_)
{
    // example:
    std::widest_type_t<FirstArg> sum;
    sum += std::forward<FirstArg>(firstArg_);
    sum += (... + std::forward<Args>(args_)); // unfold
    sum /= (sizeof...(Args) + 1);
    return sum;
}

Lets say, that every parameter type in this template is the same. For example: average of n std::int32_t's. I used imaginary widest_type_t to visualize the usage. Average calculation needs to sum every parameter, therefore, to avoid (or minimize as best as I can) overflows I need to use maximal-width type possible. Example:

  • char -> std::intmax_t
  • std::uint16_t -> std::uintmax_t
  • float -> long double (or some other type, decided by implementation)

Surely, I can write this myself, but having something like this in the standard would be nice.

Edit:
I could use moving average, however this function will be used only on small number of parameters (typically 2-8), but types I use are easily "overflowable".
Edit 2:
I also know, that for larger amounts of parameters, it would be better to use any type of array.

Poeta Kodu
  • 1,120
  • 8
  • 16
  • Should `average(1, 2)` return `1`, or `1.5`? I'm not sure I understand how this hypothetical `widest_type_t` would be formally defined. – Igor Tandetnik Feb 23 '18 at 01:58
  • 1. This will be used in terms of Color averaging. RGBA 8-bit 0-255 colors don't need to care about 0-1 values. For RGBA float 0-1 colors, it will work fine. – Poeta Kodu Feb 23 '18 at 02:01
  • I'm 99% sure there's nothing in the standard library that does exactly what you want. (I must admit I'm not even sure what it is exactly that you want.) But there should be enough building blocks for you to construct the solution, once you formalize the problem. – Igor Tandetnik Feb 23 '18 at 02:06
  • Here's an example: [pastebin - widest_type](https://pastebin.com/FztQCc7S). This is not so hard to implement, but standard library would make it work on every platform. I am not sure if my example is too. – Poeta Kodu Feb 23 '18 at 02:12
  • 1
    Instead of listing each individual type, you could perhaps use `is_integral`, `is_floating_point`, `is_signed` traits to test the type. See also `std::conditional`. Also, you are only looking at the type of the first argument: should `average(1, 2.0)` return `std::intmax_t` and not `long double`? – Igor Tandetnik Feb 23 '18 at 02:15

1 Answers1

1

Using the widest type doesn't guarantee a lack of overflow (and can still cut out fractional values when dividing), but you can extend promotion rules to do this:

template<typename T>
struct widest_type {
    static constexpr auto calculate() {
        if constexpr (std::is_floating_point_v<T>) {
            using LongDouble = long double;
            return LongDouble{};
        } else if constexpr (std::is_signed_v<T>) {
            return std::intmax_t{};
        } else if constexpr (std::is_unsigned_v<T>) {
            return std::uintmax_t{};
        } else {
            return std::declval<T>();
        }
    }

    using type = decltype(calculate());
};

template<typename T>
using widest_type_t = typename widest_type<T>::type;

template<typename FirstArg, typename... Args>
auto average(FirstArg &&firstArg_, Args&&... args_)
{
    using Common = std::common_type_t<FirstArg, Args...>;
    widest_type_t<Common> sum;
    sum += std::forward<FirstArg>(firstArg_);
    sum += (... + std::forward<Args>(args_)); // unfold
    sum /= sizeof...(args_) + 1;
    return sum;
}

If if constexpr isn't available, then std::conditional will do the trick. Likewise, std::is_foo<T>{} works in place of std::is_foo_v<T>.

I chose to keep the type trait limited to a single type since std::common_type already does a reasonable job figuring out how to combine types. You'll notice I use that and pass the result into widest_type.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Prior to C++17, we can just use SFINAE instead of `constexpr if`. I know that max width type does not guarantee, that it won't overflow, however it's best I can do. Using moving average here is not worth it. Its sad, that such a feature is not implemented in standard ``. Thanks for the answer. – Poeta Kodu Feb 23 '18 at 02:30