How to sum variadic arguments passed in to a variadic macro?

  • A+
Category:Languages

I want a program that defines a macro that can count the number of arguments and pass them to a function sum which sums the arguments' values and returns the total. I managed to do it on GCC but I want to achieve it on Visual C++ 14.

#include "stdafx.h" #include <iostream> #include <cstdarg>   #define ELEVENTH_ARGUMENT(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11 #define COUNT_ARGUMENTS(...) ELEVENTH_ARGUMENT(dummy, ## __VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)  #define SUM(...) sum(ELEVENTH_ARGUMENT(dummy, ## __VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))   int sum(int n, ...) {      int sz{ n };     va_list ap;     va_start(ap, n);     int tmp{};     while (--sz)         tmp += va_arg(ap, int);      va_end(ap);      return tmp; }  int main() {      std::cout << COUNT_ARGUMENTS(4,57,22,10,5,6,2,8,68,24,24,86,89,89,96,86) << std::endl; // 1     std::cout << SUM(5, 57, 4, 5) << std::endl; // 0     std::cout << COUNT_ARGUMENTS(5, 57, 10) << std::endl;// 1       std::cout << std::endl;     std::cin.get(); } 

I don't know what's wrong with my code, it always gives me the sum is 0.

 


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 + ...); } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: