C ++ varargs - Am I using them well or are they bad? Is there a good alternative?

The ultimate goal is to have a function that can take a variable number of arguments of a certain type (the same type, not different types) that can be declared when the function is called.

Since I'm using Visual Studio 2010, I CAN'T do:

MyFunction({1,2,3}); 

In an earlier question that was answered, I found that I could use boost::assign::list_of() , but later I found that this one has some error if you try to pass it only one parameter.

So, I did a few more searches and found that I could use variational functions to achieve what I was aiming for.

 void TestFunction2<int>(int count, ...) {} 

However, I wanted to limit it by type, so in the end I found that I can do this using templates:

 template <class T> void TestFunction(const T& count, ...); template <> void TestFunction<int>(const int& count, ...); 

Unfortunately, varargs stuff like va_list doesn't seem to look like links. In the examples I saw, to restrict types like these, reference const links. If I remove the reference aspect of the count parameter, it works the way I want, but I don’t know if this will lead to terrible side effects along the way, or if this whole wargy idea is a bad idea, start with.

So, I think my question is, what am I doing in the last example above good or bad? If this is bad, then what is a good alternative, so can I call a function with one or more parameters in a string, for example, int parameters?

+4
source share
2 answers

What you need is std::initializer_list<T> , unfortunately, this requires C ++ 11 support.

An alternative that is almost as elegant and simple enough to upgrade is to use an array :

 #include <iostream> template <typename T, size_t N> void func(T (&s)[N]) { for (size_t i = 0; i != N; ++i) { std::cout << s[i] << '\n'; } } int main() { int array[] = {1, 2, 3}; func(array); } 

When you go to a compiler that supports initializer lists, this can be changed to :

 #include <iostream> template <typename T> void func(std::initializer_list<T> s) { for (T const& t: s) { std::cout << t << '\n'; } } int main() { func({1, 2, 3}); } 

Thus, updating the functions and sites sites will be painless.

Note: the call site can be made completely similar using a macro, I advise against this approach, the estimated winnings are not worth the obfuscation.

+3
source

EDIT:

Another solution ... if your IDE library partially supports C++11 , you can initialize std::vector during the call, i.e.

 template <typename T> void TestFunction(std::vector<T> vect) { .... } .... TestFunction(std::vector<int>{1,2,3}); 

The advantages of this approach are that the STL automatically frees the allocated memory when the function goes out of scope.

If this does not work, you can resort to two liners ...

 template <typename T> void TestFunction(std::vector<T> vect) { .... } .... std::vector<int> tmp(1,2,3); TestFunction(tmp); 

The big drawback is that here the memory is on the stack until you leave this area (or explicitly resize the vector to zero length.

Both approaches have some advantages ... the counter is built-in, and you have access to other useful member functions or partner methods (e.g. std::sort ).

......................................

Why not use variable arguments? See the answer here, for example ...
Is it a good idea to use varargs in the C API to set a pair of key values?

In non-<100> compatible compilers (like your IDE) you can try ...

 template <typename T> TestFunction(const unsigned int count, T * arr) TestFunction<std::string>(10, new string[] {"One", "Two", "Three"}); 

(It seems you cannot use this in your development environment, but ...) If you are sure that you work only on modern machines and mostly use simple types, this is the best / most standards compatible with the solution ...

In C++11 you can use std::initializer , which is located in std::vector :

 #include<vector> template <typename T> void TestFunction(const std::initializer_list<T>& v) { } int main() { TestFunction<double>({1.0, 2.0}); return 0; } 

..........................

... however, this requires your compiler to be C+11 , so it was not completely portable. For anything other than simple types, it also becomes harder to read.

I understand that you are talking on the function call, but you might want to rethink this because of the readability and ease of approach to coding.

I agree with part of your approach - you want to use the template function (this handles the type variable). Before you call, you initialize your collection of elements of the same type in the temporary standard C array or std::vector / std::list (shell of the STL array).

http://www.cplusplus.com/doc/tutorial/templates/
http://www.cplusplus.com/reference/vector/
http://www.cplusplus.com/reference/list/

This is more lines of code, but it is much more readable and standardized.

i.e.

Instead of...

 MyFunction({1,2,3}); 

Using:

 template <typename T> void TestFunction(const int count, T * arr) { for (unsigned int i = 0; i < count; i++) { .... arr[i] ... ; //do stuff ... } } int main() { int * myArr = {1,2,3}; TestFuntion<int>(3, myArr); } 

... or...

 #include <vector> template <typename T> void TestFunction(std::vector<T> vect) { for (unsigned int i = 0; i < vect.size(); i++) { .... vect[i] ... ; //do stuff ... } } int main() { std::vector<int> myVect; myVect.push_back(1); myVect.push_back(2); myVect.push_back(3); TestFuntion<int>(myVect); } 

std::list would also be quite acceptable and might work better, depending on your use case.

0
source

Source: https://habr.com/ru/post/1498348/


All Articles