2

My intent is to create a function argument list of size n so I can pass it to a helper that uses a fold expression to recursively multiply the values together.

I'm a little stuck on how to make the argument list to pass to the helper. Is there a way to create a function argument list without a pack expression? Perhaps by creating an array or tuple?

Here's what I've come up with so far.

template<typename T, typename N>
T SmoothStart(const T& t, const N& n) {
    static_assert(std::is_integral_v<N>, "templatized SmoothStart requires type of N to be integral.");
    static_assert(n >= 0, "templatized SmoothStart requires value of N to be non-negative.");

    if constexpr (n == 0) {
        return 1;
    }
    if constexpr (n == 1) {
        return t;
    }
    return SmoothStart_helper((t, ...)); //<-- obviously this doesn't work but it would be awesome to have!
}

template<typename T, typename... Args>
T SmoothStart_helper(Args&&... args) {
    return (args * ...);
}
Casey
  • 10,297
  • 11
  • 59
  • 88
  • 2
    What is `t`? Do you just want `n` copies of `t`? – Justin Feb 21 '18 at 19:50
  • @Justin Yes, exactly. Say n is 5 I want an argument list composed of `SmoothStart_helper(t,t,t,t,t)` – Casey Feb 21 '18 at 19:58
  • The problem I see is that `n` is a run time value but, for this sort of solutions, you need to know it compile time. If you can make `n` a template `std::size_t` value, all goes simpler. – max66 Feb 21 '18 at 19:59
  • You might rename `SmoothStart` to `Power(const T&t)`. – Jarod42 Feb 21 '18 at 22:44
  • @Jarod42 SmoothStart is part of the easing function family (smoothstep is also one), the domain is different from Power and it would be misleading to call it Power. – Casey Feb 27 '18 at 15:45

1 Answers1

3

First off, n must be known at compile-time if you want to use a fold expression. If you move that to a template parameter, the easiest way to get a parameter pack of a size N is with std::make_index_sequence:

// The helper has to be first so that the compiler can find SmoothStart_helper().
template<typename T, std::size_t... Is>
T SmoothStart_helper(const T& t, std::index_sequence<Is...>) {
    // You were taking t by value here; I think you might want to still
    // take it by reference

    // Use the comma operator to simply discard the current index and instead
    // yield t. The cast to void is to silence a compiler warning about
    // Is being unused
    return (((void) Is, t) * ...);
}

template<std::size_t N, typename T>
T SmoothStart(const T& t) {
    // std::size_t is unsigned, so no need to check for N >= 0.
    // We also don't need to special case when N == 1. The fold
    // expression handles that case and just returns t
    return SmoothStart_helper(t, std::make_index_sequence<N>{});
}

You can then use it like so: SmoothStart<N>(myThing);.

Godbolt

Justin
  • 24,288
  • 12
  • 92
  • 142