Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you pass a varying number of arguments of the same type to a function without using arrays?

I need to pass from a minumum fo one to a maximum of seven file paths to a function. There is a convention where the file path alone is enough to identify how to handle each file.

Order of the parameters does not matter.

An obvious option to handle this (the one I currently implemented) is to pass an empty string as a parameter for unused slots.

Another one is to pass the parameters as an array or vector.

Yet another one would be to implement all possible permutations of parameters (possible, not practical).

I wonder if there is a way to simply specify that the number of paramters can vary, and then simply pass the parameters themselves.

So for example assuming that there is only one implementation of f() with special syntax to denote varying amounts of parameters

All fo the following should compile:

int main()
{
   f(file);
   f(file1, file2);
   f(file1, file3, file2, file6);
}

Is there a way to achieve this in C++ ?

like image 698
Makogan Avatar asked Dec 09 '25 06:12

Makogan


2 Answers

You can use a recursive template function.

#include <iostream>

template <typename First>
void f(First&& first) {
    std::cout << first << std::endl;
}

template <typename First, typename... Rest>
void f(First&& first, Rest&&... rest) {
    f(std::forward<First>(first));
    f(std::forward<Rest>(rest)...);
}

int main() {
    f(6,7,8,9,10);
}
like image 199
super Avatar answered Dec 11 '25 19:12

super


If you really need a variable (unbounded) number of arguments:

  • If you are on C++11 or later:

    • Use std::initializer_list (only if all the types are the same) -- see https://stackoverflow.com/a/16338804/9305398.

    • Use variadic templates (i.e. parameter packs) -- see @super's answer and/or https://stackoverflow.com/a/16338804/9305398.

  • If you are on C++03 or later:

    • Use variadic arguments -- see https://stackoverflow.com/a/1657924/9305398.

Otherwise, if you have a fixed number of (optional) parameters:

  • If you are on C++20 or later:

    • Use designated initialization as a way to have named parameters.
  • If you are on C++03 or later:

    • Use a nullable/optional type (e.g. a raw pointer, boost::optional, C++17's std::optional...) -- see @NicolBolas' answer.

    • Define all required/logical overloads (possibly using custom types) -- ugly, but this may be automated via an external code generator and/or with the preprocessor.

Otherwise, if you can use a different design to accomplish the same thing, you can do any of the following -- for C++03 and later:

  • Pass a pointer to a struct as suggested by @PaulMcKenzie.

  • Design a class that allows to set properties (through the constructor and/or methods) and then has member functions to perform operations on that data, e.g.:

    ShaderCompiler sc(vs, fs, ...);
    sc.setGeometryShader(...);
    sc.compile();
    
  • A particular nice way (see e.g. QString) is to design a class that allows to do:

    result = ShaderCompiler()
        .vertex(...)
        .fragment(...)
        ...
        .compile()
    ;
    
  • Similarly, exploiting argument-dependent lookup:

    Shader()
        << Vertex(...)
        << Fragment(...)
        ...
    ;
    
like image 29
Acorn Avatar answered Dec 11 '25 19:12

Acorn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!