I have a function that generates a safe input with error handling so it's cleaner to input numbers (int). But I want one of the argument to be an optional array of length 2 that contains the boundaries of the wanted variable input.
Here's what I have for now :
//function prototype
void input_number(int* variable, string text = "Input : ", int boundaries[2] = NULL);
//function call
int variable;
input_number(&variable, "Choose a number between 0 and 10 : ", {0, 10});
This doesn't work, throwing the error cannot convert '<brace-enclosed initializer list>' to 'int*' for argument '3' to 'void input_number(int*, std::__cxx11::string, int*)'
How can I pass an array of length 2 to the function?
Also, is int[] = NULL correct for a default array value or am I totally wrong?
The error message explains the problem - an array is converted to a pointer (to the first element) when passed as an argument to a function.
One solution would be to pass a struct type that has two members, and give that a default value. An example would be, since std::array is actually a templated structure
void input_number(int* variable, std::string text = "Input : ", const std::array<int, 2> &bounds = {});
int variable;
input_number(&variable, "Choose a number between 0 and 10 : ", {0, 10});
However, any data structure that can store two values, and be constructed using an initializer_list<int>. There are other examples of such types in the standard library - std::array is not the only one.
Personally, I'd also omit the first argument, and have it as a return value
int input_number(std::string text = "Input : ", const std::array<int, 2> &bounds = {});
// to call
int variable = input_number("Choose a number between 0 and 10 : ", {0, 10});
This has the problem of the function not being able to report an error (other than by throwing an exception), but so does your approach.
Personally, I also wouldn't pass the boundaries as default arguments. I'd simply do it with function overloading;
int input_number(std::string input, int lower_bound, int upper_bound)
{
// do whatever you want
}
int input_number(std::string input)
{
return input_number(input, std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
}
which forces the caller to either provide no arguments after the first (in which case, default bounds are used), or two (which specify the bounds) - passing only one additional argument would be a diagnosable error. The only difference from what you've asked is that this does not require (implicitly or explicitly) constructing an array or other data structure to call the function - which is arguably easier for the caller to deal with.
Try this:
void input_number(int* variable, string text = "Input : ", std::array<int, 2> boundaries = {});
Related
In the following code
#include<iostream>
template<typename T,size_t N>
void cal_size(T (&a)[N])
{
std::cout<<"size of array is: "<<N<<std::endl;
}
int main()
{
int a[]={1,2,3,4,5,6};
int b[]={1};
cal_size(a);
cal_size(b);
}
As expected the size of both the arrays gets printed. But how does N automatically gets initialized to the correct value of the array-size (arrays are being passed by reference)? How is the above code working?
N does not get "initialized" to anything. It is not a variable. It is not an object. N is a compile-time constant. N only exists during compilation. The value of N as well as the actual T is determined by the process called template argument deduction. Both T and N are deduced from the actual type of the argument you pass to your template function.
In the first call the argument type is int[6], so the compiler deduces that T == int and N == 6, generates a separate function for that and calls it. Let's name it cal_size_int_6
void cal_size_int_6(int (&a)[6])
{
std::cout << "size of array is: " << 6 << std::endl;
}
Note that there's no T and no N in this function anymore. Both were replaced by their actual deduced values at compile time.
In the first call the argument type is int[1], so the compiler deduces that T == int and N == 1, generates a separate function for that as well and calls it. Let's name it cal_size_int_1
void cal_size_int_1(int (&a)[1])
{
std::cout << "size of array is: " << 1 << std::endl;
}
Same thing here.
Your main essentially translates into
int main()
{
int a[]={1,2,3,4,5,6};
int b[]={1};
cal_size_int_6(a);
cal_size_int_1(b);
}
In other words, your cal_size template gives birth to two different functions (so called specializations of the original template), each with different values of N (and T) hardcoded into the body. That's how templates work in C++.
It works because the type of a is "array of length 6 of int" and the type of b is "array of length 1 of int". The compiler knows this, so it can call the correct function. In particular, the first call calls the template instance cal_size<6>() and the second call calls cal_size<1>(), since those are the only template instantiations which match their respective arguments.
If you attempted to call an explicit template instance, it would only work if you got the size right, otherwise the arguments wouldn't match. Consider the following:
cal_size(a); // ok, compiler figures out implicitly that N=6
cal_size<int, 6>(a); // also ok, same result as above
cal_size<int, 5>(a); // ERROR: a is not of type "array of length 5 of int"
when you declare int a[] = {1,2,3} it is the same as (or will be rewritten as) int a[3] = {1,2,3} since the templated function is receiving argument in form of T a[N], then N will have value of 3.
My code includes the following, and I get the error message above based on the last line below.
struct List {
int word_i;
int mod_i;
char mod_type;
char mod_char;
};
struct Morph {
Options mode;
deque<List> search_list;
vector<string> dictionary;
vector<bool> discovered;
string output;
int sel_word_i = 0;
bool end_found = 0;
};
// later on in a function:
morph->search_list.push_back({ morph->dictionary.size() - 1, 0, 0, 0 });
You can replace the last line with:
morph->search_list.emplace_back( morph->dictionary.size() - 1, 0, 0, 0 );
Thus the object is created not through brace initialization which does not allow narrowing conversion.
The narrowing conversion is from the return value of the call to size which returns std::size_t which is unsigned.
For why size() - 1 is not converted to a signed value see: C++ Implicit Conversion (Signed + Unsigned)
When and after you applied what Amir suggested, you may get an error saying something like, "this function does not take (3) arguments." To fix that you'll have to declare a constructor in the class, which you used for your vector, that takes that particular number of arguments. From what I understood when you replace push_back(); with emplace_back();the compiler thinks that you're trying to pass some variables to the constructor, which are the supposed arguments.
struct led {
const int loc;
int state;
unsigned int stateDuration [];
int stateMode;
unsigned long timestamp;
} led1 = {D0, 0, {500}, 0, 0},
led2 = {D7, 0, {100, 900, 400}, 0, 0};
This gives me a compilation error "too many initializers" (the array size is 0). Is it possible to declare an unsized array in the structure and then initialize it with varying values like in my example, or must I explicitly declare the array to the maximum size needed?
It is illegal to have an array of unknown bound as a non-static member of a struct. [class.mem]/13
Non-static data members shall not have incomplete types.
This is because the size of the class must be known when the closing brace is reached.
Maybe you should make stateDuration a std::vector<unsigned int> instead.
You can do it with GCC, using one its C++ language extensions, but only with the very last element of your struct, because the offsets of fields have to be constant.
See: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data. E.g. in the following, f1 is constructed as if it were declared like f2.
struct f1 {
int x; int y[];
} f1 = { 1, { 2, 3, 4 } };
If you are using something else, you can still implement the idea, but only with heap-allocated objects (so the initializer block thing will not be possible) and you will need an own new operator taking care of the dynamic part. (Perhaps delete too, I am not sure)
I have a global array, which is indexed by the values of an enum, which has an element representing number of values. The array must be initialized by a special value, which unfortunately is not a 0.
enum {
A, B, C, COUNT
};
extern const int arr[COUNT];
In a .cpp file:
const int arr[COUNT] = { -1, -1, -1 };
The enum is occasionally changed: new values added, some get removed. The error in my code, which I just fixed was an insufficient number of initialization values, which caused the rest of the array to be initialized with zeroes. I would like to put a safeguard against this kind of error.
The problem is to either guarantee that the arr is always completely initialized with the special value (the -1 in the example) or to break compilation to get the developers attention, so the array can be updated manually.
The recent C++ standards are not available (old ms compilers and some proprietary junk). Templates can be used, to an extent. STL and Boost are strongly prohibited (don't ask), but I wont mind to copy or to reimplement the needed parts.
If it turns out to be impossible, I will have to consider changing the special value to be 0, but I would like to avoid that: the special value (the -1) might be a bit too special and encoded implicitly in the rest of the code.
I would like to avoid DSL and code generation: the primary build system is jam on ms windows and it is major PITA to get anything generated there.
The best solution I can come up with is to replace arr[COUNT] with arr[], and then write a template to assert that sizeof(arr) / sizeof(int) == COUNT. This won't ensure that it's initalized to -1, but it will ensure that you've explicitly initialized the array with the correct number of elements.
C++11's static_assert would be even better, or Boost's macro version, but if you don't have either available, you'll have to come up with something on your own.
This is easy.
enum {
A, B, C, COUNT
};
extern const int (&arr)[COUNT];
const int (&arr)[COUNT] = (int[]){ -1, -1, -1};
int main() {
arr[C];
}
At first glance this appears to produce overhead, but when you examine it closely, it simply produces two names for the same variable as far as the compiler cares. So no overhead.
Here it is working: http://ideone.com/Zg32zH, and here's what happens in the error case: http://ideone.com/yq5zt3
prog.cpp:6:27: error: invalid initialization of reference of type ‘const int (&)[3]’ from expression of type ‘const int [2]’
For some compilers you may need to name the temporary
const int arr_init[] = { -1, -1, -1};
const int (&arr)[COUNT] = arr_init;
update
I've been informed the first =(int[]){-1,-1,-1} version is a compiler extension, and so the second =arr_init; version is to be preferred.
Answering my own question: while it seems to be impossible to provide the array with the right amount of initializers directly, it is really easy to just test the list of initializers for the right amount:
#define INITIALIZERS -1, -1, -1,
struct check {
check() {
const char arr[] = {INITIALIZERS};
typedef char t[sizeof(arr) == COUNT ? 1: -1];
}
};
const int arr[COUNT] = { INITIALIZERS };
Thanks #dauphic for the idea to use a variable array to count the values.
The Boost.Preprocessor library might provide something useful, but I doubt whether you will be allowed to use it and it might turn out to be unwieldy to extract from the Boost sources.
This similar question has an answer that looks helpful:
Trick : filling array values using macros (code generation)
The closest I could get to an initialization rather than a check is to use a const reference to an array, then initialize that array within a global object. It's still runtime initialization, but idk how you're using it so this may be good enough.
#include <cstring>
enum {A, B, C, COUNT};
namespace {
class ArrayHolder {
public:
int array[COUNT]; // internal array
ArrayHolder () {
// initialize to all -1s
memset(this->array, -1, sizeof(this->array));
}
};
const ArrayHolder array_holder; // static global container for the array
}
const int (&arr)[COUNT] = array_holder.array; // reference to array initailized
// by ArrayHolder constructor
You can still use the sizeof on it as you would before:
for (size_t i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) {
// do something with arr[i]
}
Edit
If the runtime initialization can never be relied on you should check your implementation details in the asm because the values of arr even when declared with an initializer may still not be known at until runtime initialization
const int arr[1] = {5};
int main() {
int local_array[arr[0]]; // use arr value as length
return 0;
}
compiling with g++ -pedantic gives the warning:
warning: ISO C++ forbids variable length array ‘local_array’ [-Wvla]
another example where compilation actually fails:
const int arr1[1] = {5};
int arr2[arr1[0]];
error: array bound is not an integer constant before ']' token
As for using an array value as a an argument to a global constructor, both constructor calls here are fine:
// [...ArrayHolder definition here...]
class IntegerWrapper{
public:
int value;
IntegerWrapper(int i) : value(i) {}
};
const int (&arr)[COUNT] = array_holder.array;
const int arr1[1] = {5};
IntegerWrapper iw1(arr1[0]); //using = {5}
IntegerWrapper iw2(arr[0]); //using const reference
Additionally the order of initalization of global variables across different source files is not defined, you can't guarantee the arr = {-1, -1, -1}; won't happen until run time. If the compiler is optimizing out the initialization, then you're relying on implementation, not the standard.
The point I really wanna stress here is: int arr[COUNT] = {-1, -1, -1}; is still runtime initialization unless it can get optimized out. The only way you could rely on it being constant would be to use C++11's constexpr but you don't have that available.
From here: https://stackoverflow.com/a/3473454/499125
int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
return arr;
}
The definition above can force the compiler to become size-aware of its parameters. This statement seems complex to me. Can someone give an anatomy of this statement or explain how does the statement achieves the size-awareness?
Can this be extended to handle multiple parameters?
It all boils down to being able to read the declaration. A simpler way of writing exactly the same would be:
typedef int int5[5]; // create a typedef
int5& fillarr( int5& ) {
}
As of the exact way of reading the original declaration, just break it into pieces:
int (&fillarr( int (&arr)[5] ))[ 5 ];
^^^^^^^^^^^^^ // A: reference to array of 5 int
^^^^^^ ^^^^^^ // B: reference to array of 5 int
^^^^^^^^ ^ // C: function taking A and returning B
You can split understanding this in two parts, 1) the return value and 2) the parameter type:
int ( &fillarr() )[5] {
static int arr[5];
return arr;
}
We can have a look at the type: int (&fillarr())[5]. To understand this, unfortunately the old C rule: "Declaration is like usage in expressions" doesn't work with references, so let's have a look at a similar declaration instead:
int (*fillarr())[5];
This is a function that returns a pointer instead of a reference to an array of size 5. How do I figure? Well, assume we do have a function f that returns a pointer to an array of ints, how would we access the fifth element? Let's see, we first have to call the function: f(). Then we have to dereference the result *f() and then access the fifth element (which doesn't exist, but ignore that) (*f())[5] which is an int. This is exactly the declaration syntax:
int x;
int (*f())[5];
x = (*f())[5];
// ^^^-------- call f -> yields pointer to array of 5 ints
// ^----------- dereferene the result of f -> yields array of 5 ints
// ^^^---- access the fifth element
Now you only substitute the * for an & because you're returning a reference in your example.
The parameter syntax works analogously.