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.
It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.
macro expansion possible specifying __VA_ARGS__ The '...' in the parameter list represents the variadic data when the macro is invoked and the __VA_ARGS__ in the expansion represents the variadic data in the expansion of the macro. Variadic data is of the form of 1 or more preprocessor tokens separated by commas.
__VA_OPT__ is a new feature of variadic macros in C++20. It lets you optionally insert tokens depending on if a variadic macro is invoked with additional arguments. An example usage is comma elision in a standardized manner.
To support variable length arguments in macro, we must include ellipses (…) in macro definition. There is also “__VA_ARGS__” preprocessing identifier which takes care of variable length argument substitutions which are provided to 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With