How to sum variadic arguments passed in to a variadic macro?
Don't use a variadic macro. Visual C++ 14 (or 2015) is a C++11/14 compliant compiler. That means it it supports variadic templates. You can easily recurse a parameter pack to get the sum of the parameters and getting the count can be done by using sizeof...
. This lets you write count
as
template<typename... Args>
auto count(Args&&...)
{
return sizeof...(Args);
}
and then sum
can be written as
// base case
template<typename T>
auto sum(T&& first)
{
return first;
}
// multiple parameters case
template<typename T, typename... Args>
auto sum(T&& first, Args&&... rest)
{
return first + sum(rest...);
}
using those in
int main()
{
std::cout << count(3,4,5) << "\n";
std::cout << sum(3,4,5);
}
prints
3
12
which you can see in this live example.
As suggested by HolyBlackCat you can use the dummy array trick to avoid using recursion. That would give you a sum
that looks like
template <typename ...P>
auto sum(const P &... params)
{
using dummy_array = int[];
std::common_type_t<P...> ret{}; // common_type_t is to find the appropriate type all of the parameter can be added to
(void)dummy_array{(void(ret += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
return ret;
}
Do note though that this might not work correctly for all types, like shown here with std::valarray
. Changing it to
template <typename T, typename ...P>
auto sum(T first, P&&... rest) // copy the first parameter to use it as the accumulator
{
using dummy_array = int[];
(void)dummy_array{(void(first += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
return first;
}
should be more correct, although it could probably be improved some more (suggestions/edits welcomed)
If you can use a C++17 complaint compiler then sum
can be even further simplified using a fold expression like
template<typename... Args>
auto sum(Args&&... rest)
{
return (rest + ...);
}
Adding onto @NathanOliver, if you'd like to use variadic templates without recursion, std::initializer_list
and std::common_type
are both available in C++11, allowing you to do this instead:
template <typename... Args,
typename T = typename std::common_type<Args...>::type>
T sum(Args&&... args) {
std::initializer_list<T> l{args...};
return std::accumulate(l.begin(), l.end(), T{});
}
Edit: Note that this solution will allow sum to be invoked on all types that are implicitly convertible to a common type (that is what common_type
does).
For example:
sum(1, 2, 3) // Ok, all ints
sum(1, 2, true) // Ok, bool converts to int
sum(1, 2, 3.) // Error, int to double is a narrowing conversion