Home > database >  Reverse order of varidic template arguments while constructing a map key
Reverse order of varidic template arguments while constructing a map key

Time:10-07

I am using a variadic template to construct a key for a map, calculating a number to a base:

template<typename T>
uint64_t key(int base, T n)
{
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t key(int base, T n, Args... rest)
{
    return key(base, rest...) * base   (uint64_t(n) % base);
}

Calling it with key(10, 1, 2, 3) gives me a key with decimal value 321. I would prefer to get 123 for the key, and I have found a solution that works:

template<typename T>
uint64_t keyHelper(int& mul, int base, T n)
{
    mul = base;
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t keyHelper(int& mul, int base, T n, Args... rest)
{
    int mul_tmp;
    uint64_t result = keyHelper(mul_tmp, base, rest...)  
        (uint64_t(n) % base) * mul_tmp;
    mul = mul_tmp * base;
    return result;
}

template<typename... Args>
uint64_t key(int base, Args... args)
{
    int mul;
    return keyHelper(mul, base, args...);
}

This solution feels like a hack, tho, since it passes around a reference to fix the exponent of multiplications. Is there a simple varidic way where the template calculates the number in the required order, i.e. 123? I have seen solutions for reversing variadic arguments, and they seem overly complicated.

CodePudding user response:

Since C 17 I would use fold expression (op,...) to do that:

template<class B, class ... Args>
auto key(B base, Args ... args) {
    std::common_type_t<Args...> res{};
    ( (res *= base, res  = args % base), ... );
    return res;
}

Demo

CodePudding user response:

How about this:

template<typename T>
uint64_t key_impl(int base, unsigned int exp, T n)
{
    return uint64_t(n) % base;
}

template<typename T, typename... Args>
uint64_t key_impl(int base, unsigned int exp, T n, Args... rest)
{
    uint64_t res = uint64_t(n) % base;
    for (unsigned int i = 0u; i < exp;   i)
        res *= base;
    return key_impl(base, exp - 1u, rest...)   res;
}

template<typename... Args>
uint64_t key(int base, Args... args)
{
    return key_impl(base, sizeof...(Args) - 1u, args...);
}

CodePudding user response:

Since the number of arguments is known, the maximum exponent is known, and you can just use that to calculate the sum

template<typename T, typename... Args>
uint64_t key(int base, T n, Args... rest)
{
    return (uint64_t(n) % base) * std::pow(base, sizeof...(rest))
              key(base, rest...);
}

(the base case is unchanged)

demo

Note that you shouldn't actually use std::pow here; write a function that does integer exponentiation instead.

  • Related