Yuri Krzyzhanovski
Yuri Krzyzhanovski

Reputation: 13

How do I write a function with variable number of arguments using pointers?

So I have a task to write a function "mult" with variable number of arguments using pointers. And this function must calculate the product of float numbers.

I've followed the guide that our uni gave us but my product is still equal to zero. I found out that the problem is that every other number to be multiplied is zero.

#include <iostream>


using namespace  std;

int mult(int k,...){
    int* p = &k;
    int m = 1;
    for(; k != 0; k--){
        m *= *(++p);
    }
    return m;
}

int main(){
    float res1 = mult(11,45,10,9,8,7,6,5,4,3,2,2);
    float res2 = mult(7,12,23,0.3,0.6,1,2);
    float res3 = mult(3,0.6,-12,-0.9);
    cout << "Your results are:\n"
         <<res1<<"\n"
         <<res2<<"\n"
         <<res3<<"\n";

    return 0;
}

sample output

Here's examples from guide:

void Print_stor(int k, ...)
{
 int n=k;
 int a[n];
 int *p = &k;
 for ( ; k!=0;k--)
 a[k-1]=*(++p);
 for(int i=n-1; i>=0; i--)
 printf("%i ", a[i]);
 printf("\n");
}



int sum(int k, …)
 {
 int *p = &k;
 int s=0;
 for ( ; k!=0; k--)
 s+=*(++p);
 return s;

Upvotes: 1

Views: 173

Answers (4)

Rane
Rane

Reputation: 371

With C++17's fold expressions functions like the one you described are really simple to create:

template<typename... Args>
auto mult(Args... args)
{
    // Parenthesis are required
    return (... * args); 
}

The fold expression above expands as:

((arg_1 * arg_2) * arg_3) * ...) * arg_n)

Any binary operator can be used to replace the multiplication:

// Sum
return (... + args);

// and
return (... && args);

You can also perform rather complicated stuff with these:

template<typename T, typename... Args>
void comma_sep_print(const T& first, const Args&... args)
{
    std::cout << first;
 
    // Fold over comma operator
    ((std::cout << ", " << args), ...) << '\n'; 
}

comma_sep_print(1, 2, 3);
// Outputs: 1, 2, 3

Albeit the fold over comma in comma_sep_print looks strange, the expansion is quite straightforward:

(std::cout << ", " << 2, std::cout << ", " << 3) << '\n`;

Upvotes: 0

psnx
psnx

Reputation: 668

Well, if you must use a pointer, here is a solution with one:

#include <functional>
#include <initializer_list>
#include <iostream>
#include <numeric>
#include <memory> 

template<typename T, typename... Args>
T mult(T t, Args... args)
{
    std::unique_ptr<std::initializer_list<T>> p = std::make_unique<std::initializer_list<T>>(std::initializer_list<T>{args...});
    return std::accumulate(p->begin(), p->end(), t, [](auto a, auto b){return a*b;});
}

int main()
{
    std::cout << mult<float>(1.0,2.0,3.0,4.0);
    return 0;
}

Upvotes: 0

Surt
Surt

Reputation: 16099

I would have so many worries over this code's validity.

  1. Are the ellipse parameters passed in registers or on the stack. Calling conventions for 64-bit says that the first couple of pointer/int/etc are passed in registers.
  2. Even if the ellipse parameters are passed on the stack, the k is certainly not, it would be placed in a register. And so you can't take its address.
  3. Ellipse parameters are default argument promoted which means all floats are converted to doubles, so when you try to convert to int you get half a double.

I would think this only works because of some undefined behaviour if at all, and I doubt even that because of pt. 3.

Upvotes: 0

Cory Kramer
Cory Kramer

Reputation: 117866

You could write mult as a variadic function, and use std::accumulate with std::multiplies as the operator.

#include <functional>
#include <initializer_list>
#include <iostream>
#include <numeric>

template<typename T, typename... Args>
T mult(T t, Args... args)
{
    std::initializer_list<T> values{args...};
    return std::accumulate(values.begin(), values.end(), t, std::multiplies<T>());
}

int main()
{
    std::cout << mult<float>(11,45,10,9,8,7,6,5,4,3,2,2);
    return 0;
}

Output

3.59251e+09

Upvotes: 5

Related Questions