0

I am trying to adapt some Java code to C++ and they use variadic parameters in a method. In their code, they are able to iterate through the parameter as a list in a for loop. Is there any way to have similar behavior in C++?

I'm having some trouble wrapping my head around the concept, and I feel like I may have a fundamental misunderstanding as to how this is implemented in C++. I saw some similar code online that seemed to convert the parameter list into a vector, which I tried implementing below (note that I need a vector of pointers so that I can call child object implementations of the accept() method).

std::string AstPrinter::parenthesize(std::string name, Expr<std::string> exprs...)
{
    std::vector<Expr<std::string>*> exprVec = { exprs... };
    name = "(" + name;
    for (Expr<std::string>* expr : exprVec)
    {
        name += " ";
        name += expr->accept(this);
    }

    name += ")";
    return name;    
}

The code gives these errors on line 52:

no instance of constructor "std::vector<_Ty, _Alloc>::vector [with _Ty=Expr<std::string> *, _Alloc=std::allocator<Expr<std::string> *>]" matches the argument list

expected a }

cannot convert from 'initializer list' to 'std::vector<Expr<std::string> *,std::allocator<Expr<std::string> *>>'

I don't really need it to be in a vector. I am just wondering as to how I can access members of the argument list so that I can call their version of the accept() method.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
diego_rod
  • 39
  • 1
  • 6

2 Answers2

2

There are 3 different ways to accept a variable number of arguments.

First, C style varargs. You probably do not want this.

Second, C++ variadic templates. If everything is the same type, probably overkill.

Lastly, something like a std::initializer_list if the data is const. Otherwise, just a std::vector.

std::string AstPrinter::parenthesize(std::string name, std::vector<Expr<std::string>> exprs)

At the call site, do printer.parenthesize("foo", {Expr0, Expr1, Expr2});. Notice the extra {}.

This is the simplest way to solve your problem.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thank you! This is incredibly useful. Does the {expr1, expr2, expr3} syntax work for arrays as well? – diego_rod Oct 13 '21 at 01:12
  • @diego yes, but C++ arrays are fixed size. Vector is what C++ calls a variable length array. – Yakk - Adam Nevraumont Oct 13 '21 at 01:22
  • 2
    A Variadic template would work just fine and not be overkill. Especially when coupled with fold expressions. – Remy Lebeau Oct 13 '21 at 04:40
  • @RemyLebeau A list of identical objects taken by value by a beginner at C++? I'd say it is overkill. – Yakk - Adam Nevraumont Oct 13 '21 at 13:45
  • @Yakk-AdamNevraumont variadic template arguments don't have to be passed by value, they can be [passed by reference/pointer](https://stackoverflow.com/questions/6361574/) instead. – Remy Lebeau Oct 13 '21 at 14:23
  • @RemyLebeau I am unsure why you stated that true fact, as it seems irrelevant to the comment you replied to. The OP seems to want to take a list of identical type values (but who knows, they are a java programmer, and java does not have the same kind of value/reference/pointer syntax as C++, so they often get it confused), so my point is that variardic templates are overkill for what the OP's code seems to want to do. In another situation they would not be overkill. – Yakk - Adam Nevraumont Oct 13 '21 at 14:31
0

In C++17 and later, you can avoid the need for copying the variadic arguments into a vector if you use a fold expression, eg:

template <typename... Args>
std::string AstPrinter::parenthesize(const std::string& name, const Args&... exprs)
{
    return "("
        + name
        + (
            (" " + exprs.accept(this)) + ... // <-- here
        )
        + ")";
}

Online Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770