I'm trying to find a good solution for the following problem:
I want to implement a function that takes a variable number of container arguments and returns the size of the biggest container. Here is an example:
std::vector<std::string> vStr(2, "foo");
std::vector<int> vInt(1, 123);
std::vector<double> vDouble(3, 1.1);
std::list<char> lChar(4, '*');
// or even more container
size_t uiMaxSize = getMaxContainerSize(vStr, vInt, vDouble, lChar /*, ...*/);
in this case getMaxContainerSize should return 4, because lChar has the biggest size of 4.
I've already implemented this workaround using cstdarg:
#include <cstdarg>
...
size_t getMaxContainerSize(int iCnt, ... )
{
size_t uiMaxSize = 0;
va_list ap;
va_start(ap, iCnt);
for(int i=0; i<iCnt; i++)
{
size_t uiTempSize = va_arg(ap, size_t);
uiMaxSize = uiMaxSize<uiTempSize ? uiTempSize : uiMaxSize;
}
va_end(ap);
return uiMaxSize;
}
...
size_t uiMaxSize = getMaxContainerSize( 4, vStr.size(), vInt.size(), vDouble.size(), lChar.size());
But with this I have to type .size() for every container and I also have to specify the number of containers. I also don't like to use C stuff in C++ programs and I'm asking myself if there is a better way to implement this. Maybe by using some class and overloading operator<<() so I can type something like this:
MaxSizeFinder cFinder;
cFinder << vStr << vInt << vDouble << lChar;
size_t uiMaxSize = cFinder.getResult();
Do you think something like this is possible? Any suggestions?
Thank you.
Use a variadic template:
template<typename... Conts>
std::ptrdiff_t getMaxContainerSize(const Conts&... conts) {
return std::max({conts.size()...});
}
When you pass containers as arguments, the compiler will deduce a list of types for Conts. Each parameter of the function will be a const <deduced type> &*. Using conts.size()... expands to conts1.size(), conts2.size(), ..., contsN.size(), where conts# is each argument given to the function. It turns out std::max has a handy overload that you can delegate this to.
There are a couple key advantages of variadic templates over C variadic functions:
They are type safe - the compiler is guaranteed to complain when types don't match, and you don't need a format string or anything.
The function knows how many arguments were passed, and you can get it with sizeof...(Conts).
Nothing special happens to the arguments when going in. In a variadic function, char would be an int by the time the function has to pick it out, among others.
You don't need to explicitly specify any of the types when you use the arguments. This means you can accept an infinite number of types instead of a predefined list (think printf's format specifiers).
Finally, per the comments, the return type was changed to a signed type that mostly acts as the signed counterpart to size_t (sort of like the non-standard ssize_t).
To future-proof the answer, there will soon be a std::size for a more generic way to get a container's size:
using std::size;
return std::max({size(conts)...});
This expands similar to above: size(conts1), size(conts2), ..., size(contsN)
*Normally, parameter packs are used with T&&... with std::forward instead of const T&.... This would potentially buy you something with third-party classes that have a more efficient size function when the object used is an rvalue. However, it adds complexity in general for a low chance at any benefit.
Related
I have a function like this:
typedef std::vector<std::map<int,std::string>> MyVec1;
typedef std::vector<std::map<std::string,std::string>> MyVec2;
enum options { DEFAULT_OPTIONS = 0x0000000u,
OTHERS = 0x0000001u };
void func(std::string s1, MyVec1& vec1, MyVec2& vec2, bool type,
std::string s2, options opt, uint32_t opt2){
//do something
//And put data to the vectors
}
I want to create a luncher which can take any number of arguments (upto 7) and set default values for the arguments that are not passed and then call the func() with all arguments, kinda like this:
void launcher(args...){
//If any string is passed, pass it as the 1st arg of func, or an empty string
//If any second string is passed, pass it as the 5th arg of func or an empty string
//If any MyVec1 type is passed, pass it as the second arg or a dummy vector.
//If any MyVec2 type is passed, pass it as the third arg or a dummy vector.
////By dummy vector I mean create a vector with MyVec1 vec; and pass it as arg.
////Like this, if other args are passed they are passed to the func with
////given value otherwise a default value is chosen
////And finally call func() with all arguments.
}
The same can be achieved by overloading and using default values for arguments but that's a huge task if you consider that I want those arguments to be non-sequential too (specially the vectors) i.e launcher will manage if you pass MyVec2 first and MyVec1 as the second.
I had to create 16 overload for a three vector function (3-vector: 6, 2-vector: 6, 1-vector: 3, 0-vector: 1).
Is this even possible?
Note:
I think I can detect the type with this. But the rest seems kinda impossible to me.
If it is not possible to work with reference then that will do too. I have other functions that needs the same which doesn't work with references (A function with lots of args like the above without those vectors).
A naive implementation of what you suggested is actually fairly straightforward - you basically need something that gets the Ith T out of a tuple, or some default value if it doesn't exist. Add some forwarding pixie dust and you are done.
But it's going to be hard to maintain, and there are thorny design issues with arguments that require a conversion. "foo" by C++'s overload resolution rules converts better to bool than to std::string. Do you want to follow those rules or design your own?
Instead, do something like the named parameters idiom:
struct launcher {
std::string s1 = "";
MyVec1* p_vec1 = nullptr;
MyVec2* p_vec2 = nullptr;
bool type = false;
std::string s2 = "";
options opt = DEFAULT_OPTIONS;
uint32_t opt2 = 0;
launcher& set_s1(std::string& s) { s1 = s; return *this; }
launcher& set_vec1(MyVec1& vec1) { p_vec1 = &vec1; return *this; }
// etc.
void launch(){
MyVec1 vec1; MyVec2 vec2;
MyVec1& v1 = p_vec1 ? *p_vec1 : vec1;
MyVec2& v2 = p_vec2 ? *p_vec2 : vec2;
return func(s1, v1, v2, type, s2, opt, opt2);
}
};
I assume your intention behind launcher is to deliver a more useable interface to func. Based on this, I wouldn't rely just one a bunch of overloads or templates. You could push a little work to the runtime, but simplify the interface a lot by using simple built in pointers.
void launcher(std::string* s1, MyVec1* vec1, MyVec2* vec2, bool* type,
std::string* s2, options* opt, uint32_t* opt2){
//is s1 set?
if (s1 != nullptr) // if(s1) would also be ok
/* Construct the default parameters based on what you get*/
/* not enough parameters to call func?*/
throw std::invalid_argument("You need to specify at least ...");
You could also replace the built-in pointers with boost::optional, since it's more clear what your intentions are and boost also makes some optimizing.
You could mix this runtime checks, with overloading and templates, but I guess, launcher will not be called in a loop for scientific calculations, isn't it? And your goal is a simple interface.
I have an array of vectors in one class:
class MeasurementData
{
private:
std::vector<double> m_measuredStrengths[3];
}
And I would like a function of another class to examine that and pass back an integer based on the analysis, e.g.
int CStrengthAnalyser::GetBestFit(std::vector<double> measuredStrengths[3])
{
int bestFit = -1;
// do stuff
return bestFit;
}
And I'm a little confused by the best practice for passing this kind of object around, plus setting up my receiving function to guarantee no changes to the original data.
Is my function declaration OK as-is, or do I need to add some best practice tweaks?
The function you have right now is the same function as:
int CStrengthAnalyser::GetBestFit(std::vector<double> * measuredStrengths )
So it can definitely modify the vectors. If you're always dealing with an array of size 3 you can take a const reference to an array of size 3.
int CStrengthAnalyser::GetBestFit(std::vector<double> const (&measuredStrengths)[3])
Or if you want to make it more generic:
struct CStrengthAnalyser
{
// ...
template<std::size_t N>
int GetBestFit(std::vector<double> const (&measuredStrengths)[N])
{ ... }
};
In this case the member function definition must appear in the header (or, to be precise, the definition must be visible to the compiler at the callsite).
If you want to avoid the ugly reference to array syntax you could change the MeasurementData definition to contain a std::array<std::vector<double>, 3> instead of a plain C array. Then passing a reference to that is cleaner
int CStrengthAnalyser::GetBestFit(std::array<std::vector<double>, 3> const& measuredStrengths)
And finally, you could also deduce the size of the std::array using a function template as shown previously.
I would suggest that you use a vector of vectors here, like
vector<vector<double> > your_measure(3);
When you pass it into another function, you can use the key word const to it, like
my_fun(vector<vector<double> > const & your_vec_vec);
I'm trying to write a function for enumerating through a number of a specific base, where the number is stored in some kind of list. Here is an example, taking a std::vector
void next_value(std::vector<unsigned int> &num, unsigned int base) {
unsigned int carry = 1;
for (unsigned int &n: num) {
n += carry;
if (n >= base) {
carry = 1;
n = 0;
} else {
carry = 0;
}
}
}
The num vector doesn't necessarily need to be a vector, it can be an array, or actually any type that has a std::begin() and std::end() defined for it. Is there a way to express that num can be anything with begin() and end(), but that it must have unsigned int type for its elements?
If you really want to check this, try:
template <class Sequence>
void next_value(Sequence &num, unsigned int base) {
static_assert(boost::is_same<Sequence::value_type, unsigned>::value, "foo");
// ...
If you're not using C++11 yet, use BOOST_STATIC_ASSERT instead.
If you need to support plain C-style arrays, a bit more work is needed.
On the other hand, #IgorTandetnik correctly points out that you probably do not need to explicitly check at all. The compiler will give you an (ugly) error if you pass a type which is truly unusable.
Writing a generic function with a static_assert is a good idea, because you can give the user a helpful error message rather than "foo".
However there is another approach using C++11:
template <typename Container, typename ValueType>
typename std::enable_if<std::is_same<Container::value_type, ValueType>::value, void>::type
next_value(Container& num, ValueType base)
{
// ...
}
This is a rather cryptic approach if you've never seen this before. This uses "Substitution failure is not an error" (SFINAE for short). If the ValueType doesn't match the Container::value_type, this template does not form a valid function definition and is therefore ignored. The compiler behaves as if there is not such function. I.e., the user can't use the function with an invalid combination of Container and ValueType.
Note that I do recommend using the static_assert! If you put a reasonable error message there, the user will thank you a thousand times.
I would not in your case.
Change carry to a book, use ++ instead of +=, make base a type T, and n an auto&.
Finally, return carry.
Your code now ducktypes exactly the requirements.
If you want diagnostics, static assert that the operations make sense with custom error messages.
This let's your code handle unsigned ints, polynomials, bigints, whatever.
I'ld like to create a function that passes not a std::string by reference to be modified,
void changeStr(std::string &str)
{
str = "Hello World!";
}
, but rather an entire, fixed-sized array of std::strings (the function will do exactly the same: attribute some specific strings to each space in the array). But I don't know which is the appropriate syntax...
Since you're using C++ you probably want to pass a collection of values by reference instead of a collection of references by reference. The easiest way to achieve this is to use std::vector<T>
void changeStr(std::vector<std::string>& collection) {
if (collection.size() > 0) {
collection[0] = "hello world";
}
}
Here's how!
//Change the 10 to whatever size you'd like
void changeStr(std::string (&str)[10]) {
}
This of course, is for a static size, the other answers, however, are better methods accomplishing what you need with flexibility.
One approach would be to pass a reference to an std::array<std::string, N> where N is the size of the array. You can use a function template to deduce N:
#include <array>
template <size_t N>
void changeStr(std::array<std::string, N>& strings)
{
// access strings[i] for i >= 0 and i < N
}
alternatively, you can pass fixed size plain arrays, again using function template:
template<size_t N >
void changeStr( std::string (&strings)[N] )
{
// access strings[i] for i >= 0 and i < N
}
Note that the template is necessary here to allow for the function to work with fixed sized arrays of different sizes. The template allows you to keep the size information without having to worry about it's actual value.
void changeStr(std::string pStrings[], int num)
You can pass any C array of any size. If the changeStr function needs to know the size, you need to pass it as a size parameter.
Note that personally I prefer to use a vector.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Variable number of arguments in C++?
May I not set the number of arguments of a function with variable number of arguments? As an example: can the following interface be implemented?
int sum(...) { ... }
sum(1, 2, 3, 4); // return 10
Conventional variadic functions are messy and not type-safe, but in C++11 you can do this cleanly using variadic templates and (compile-time) recursion:
// Base case for recursion
template <typename T>
inline T sum(T n) {
return n;
}
// Recursive case
template <typename T, typename... Args>
inline T sum(T n, Args... args) {
return n + sum(args...);
}
Since it's a template, this'll work for any types that have an operator+ defined:
std::cout << sum(1, 2, 3) << std::endl; // Prints 6
std::cout << sum(3.14, 2.72) << std::endl; // Prints 5.86
However, because the return type of the recursive template function is taken from the first argument only, you can get suprising results if you mix different argument types in one call: sum(2.5, 2) returns 4.5 as expected, but sum(2, 2.5) returns 2 because the return type is int, not double. If you want to be fancy, you can use the new alternative function syntax to specify that the return type is whatever the natural type of n + sum(args...) would be:
// Recursive case
template <typename T, typename... Args>
inline auto sum(T n, Args... args) -> decltype(n + sum(args...)) {
return n + sum(args...);
}
Now sum(2.5, 2) and sum(2, 2.5) both return 4.5.
If your actual logic is more complex than summation, and you don't want it inlined, you can use the inline template functions to put all the values into some sort of container (such as a std::vector or std::array) and pass that into the non-inline function to do the real work at the end.
You probably want to do this by writing the function to take something like a vector<int>, which you'll construct on the fly with a braced initializer list:
int sum(std::vector<int> const &n) {
return std::accumulate(begin(n), end(n), 0);
}
If there's some possibility the numbers might be (for example) floating point instead, you probably want to write it as a template instead:
template <class T>
T sum(std::vector<T> const &n) {
return std::accumulate(begin(n), end(n), T());
}
Either way, you'd invoke this just marginally differently:
int x = sum({1,2,3,4});
Warning: this feature was added to C++ fairly recently, so some compilers (e.g., VC++) don't support it yet -- though others (e.g., g++ 4.7+), do.
No, you can't.
Just don't use variable arguments. They suck in every conceivable fashion and are completely not worth anybody's time.
A C++ variadic function must know how many (and what type) of arguments it was passed. For example, printf's format string tells it what extra arguments to expect.
Your sum has no way of knowing if it got 4 ints or 10. You could make the 1st argument a length:
int sum(int howmany, ...) { ... }
so the function knows how many ints follow. But really you should just pass an array (or vector if you're feeling C++'y)
There are multiple ways to solve your issue. I'll go over a few:
Method 1:
-Create a series of overloaded sum functions to suit your needs.
Cons
-code bloat
This can be implemented by making multiple functions with headers:
int sum(int a);
int sum(int a, int b);
int sum(int a, int b, int c);
etc...
Method 2:
-create a custom class with a linked list, and pass in a pointer to the head of the linked list. This is probably your best move in this case, assuming you don't know the amount of data to be passed in.
Function header:
int sum(LinkedList *headPointer);
Method 3:
-pass in an array of variables
Function header:
int sum(int input[]);
Method 4:
-create a function with auto-set variables
Function header:
int sum(int a=0, int b=0, int c=0, int d=0,... int n=0);