suppose I'm passing an argument to a function like this:
void myFunc( int* arrayOfInts );
this doesn't seem safe in the sense that the function does not know the size of the array of ints. I can put a comment saying "this function assumes it's getting an array of 10 ints" but it is not possible to check it. So wrong usage of this function can lead to the mysterious "segmentation fault". Is there a better way to do this?
Pass in the size of the array as well, C-style:
void myFunc(int* arrayOfInts, int arraySize);
Or pass in a pointer to after the end of the array, iterator-style:
void myFunc(int* begin, int* end);
Or pass in a std::vector<int> or equivalent and sidestep the problem entirely:
void myFunc(const std::vector<int>& vec);
In C and C++, when dealing with raw arrays, functions generally require that you pass in the size of the array as well, like
void foo(int *arr, size_t size_);
But in C++, why don't you use std::vector anyways? It's safe.
use template function
template <int N>
void fmyFunc(int (& array)[N])
{
}
Or STL container(is better way in modern C++ code)
void fmyFunc(const std::vector<int>& v)
{
}
The old way is to pass in array size
void fmyFunc(int* arrayOfInts, int size)
{
}
Straightforward C++ answer is to use std::vector.
Traditional C/C++ answer is to accept array size as an argument as well.
There is a third answer, specific for C++: you can use a function template specialized for array size. It does not mean you should. For details, see the accepted answer at c++ function template specialization for known size typedefed array.
You could take the argument by reference to an array instead, and make the size of the array a template parameter N:
template <int N>
void myFunc(int (&arrayOfInts)[N])
{
// here we know the size is N
}
Alternatively, if your function should only accept arrays of a certain size (e.g. a 3D-point which is an array of size 3), specify that size instead of using a template. This way the compiler will only let you call this function with a valid array:
void myFunc(int (&arrayOfExactlyThreeInts)[3])
{
}
Consider using a second parameter that indicates the size, or even better use an std::vector<int> that will allow you to query the size inside the function
Either pass the size, or, preferably, use std::vector<int>.
As others have said, add an argument with the size of the array. The reason for this is so that you can write one function for arrays of different sizes. The approach of writing a template function that figures out the size of the array means that you have a different function for each array size that's passed to the template. And, yes, a vector carries its own size around, but adds overhead to be able to do that. Sometimes simplest is best.
Related
What's the canonical way to get the reference to std::array's underlying raw (C) array?
The data() method returns just a raw pointer, which makes it unsuitable e.g. for passing into functions which accept a reference to a raw array of a known size.
Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?
What's the canonical way to get an std::array's underlying raw (C)
array?
There is no way of getting the underlying C array.
Also, is there a good reason why data() returns a raw pointer, and not
a reference to the underlying raw array, or is this just an oversight?
It's backwards: there is no good reason for the std::array to provide the underlying C array. As you already said, the C array would be useful (over the raw pointer) only with functions getting a reference to C arrays.
When was the last time you had a function:
void foo(int (&arr)[5])
Me? Never. I never saw a function with a C array reference parameter with the exception of getting the size of array (and rejecting pointers):
template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }
Let's dive a little into why parameters references to arrays are not used.
For starters, from the C area pointer with a separate size parameter was the only way to pass arrays around, due to array-to-pointer decay and lack of reference type.
In C++ there are alternatives to C arrays, like std::vector and std::array. But even when you have a (legacy) C array you have 2 situations:
if you pass it to a C function you don't have the option of reference, so you are stuck to pointer + size
when you want to pass it to a C++ function the idiomatic C++ way is to pass begin + end pointers.
First of all a begin + end iterators is generic, it accepts any kind of containers. But is not uncommon to see reference to std::vector when you want to avoid templates, so why not reference to C array if you have one? Because of a big drawback: you have to know the size of the array:
void foo(int (&arr)[5])
which is extremely limiting.
To get around this you need to make it a template:
template <std::size N>
void foo(int (&arr)[N])
which beats the purpose of avoiding templates, so you better go with begin + end template iterators instead.
In some cases (e.g. math calculations on just 2 or 3 values which have
the same semantics, so they shouldn't be separate parameters) a
specific array size is called for, and making the function generic
wouldn't make sense. In those cases, specifying the size of the array
guarantees safety since it only allows passing in an array of the
correct size at compile-time; therefore it's advantageous and isn't a
"big drawback"
One of the beauties of (C and) C++ is the enormous scope of applicability. So yes, you will always find some fields that use or need a certain unique feature in an unique way. That being said, even in your example I would still shy away from arrays. When you have a fixed number of values that shouldn't be semantically separated I think a structure would be the correct choice over arrays most of the time (e.g. glm::mat4 instead of float[4]).
But let's not forget what std::array is: a modern replacement for C arrays. One thing I learned when analyzing options is that there is no absolute "better than". There is always a "depends". But not in this case: std::array should unquestionably replace C arrays in interfaces. So in the rare case where a fixed size container is needed as a reference parameter it doesn't make sense to enable encouraging the use of C arrays when you already have an std::array. So the only valid case where exposing the underlying C array of std::array is need is for some old libraries that have C array reference parameters. But I think that in the bigger picture adding this to the interface it is not justified. New code should use a struct (btw std::tuple is getting easier and easier to use by each standard) or std::array.
AFAIK, There's no direct or typesafe way to do it, but one work around if you need to pass to a function (with a signature you cannot change to std::array) is by usingreinterpret_cast like this:
some_function(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data())));
If you wanted to make it safer:
#include <array>
void passarray(int (&myarr)[5]){}
template <typename ValueT, std::size_t size>
using CArray = ValueT[size];
template <typename ValueT, std::size_t size>
CArray<ValueT, size> & c_array_cast(std::array<ValueT, size> & arg) {
{
return *reinterpret_cast<CArray<ValueT,size>*>(arg.data());
}
int main()
{
std::array<int,5> myarr = { {1,2,3,4,5} };
passarray(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data()));
passarray(c_array_cast(myarr));
return 0;
}
There isn't one.
I can see why it would be useful, especially when working with legacy code, but since a couple of decades ago we're supposed to be moving away from code like that and towards iterator-aware algorithms. And when working with C code you'd have to use a pointer anyway. I presume these are factors in the decision not to provide this functionality.
Rewrite your code to accept std::array<T, N>& instead, if possible.
You can reinterpret_cast the .data() to a raw, like:
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
But it is an ugly hack. As the others have already suggested, I recommend you to use std::array as-is.
Usage:
#include <cstdint>
#include <array>
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
void foo(const std::uint8_t(&buf)[5]){
// ...
}
int main(void){
std::array<std::uint8_t, 5> arr = {1,2,3,4,5};
foo(to_raw_array(arr));
}
Why not passing std::array.begin()? Worked in SDL2 on:
int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)
My line to be drawed:
std::array<SDL_Point, 8> a_line;
I passed like this:
SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);
What's the canonical way to get the reference to std::array's underlying raw (C) array?
The data() method returns just a raw pointer, which makes it unsuitable e.g. for passing into functions which accept a reference to a raw array of a known size.
Also, is there a good reason why data() returns a raw pointer, and not a reference to the underlying raw array, or is this just an oversight?
What's the canonical way to get an std::array's underlying raw (C)
array?
There is no way of getting the underlying C array.
Also, is there a good reason why data() returns a raw pointer, and not
a reference to the underlying raw array, or is this just an oversight?
It's backwards: there is no good reason for the std::array to provide the underlying C array. As you already said, the C array would be useful (over the raw pointer) only with functions getting a reference to C arrays.
When was the last time you had a function:
void foo(int (&arr)[5])
Me? Never. I never saw a function with a C array reference parameter with the exception of getting the size of array (and rejecting pointers):
template <class T, std::size_t N>
auto safe_array_size(T (&)[N]) { return N; }
Let's dive a little into why parameters references to arrays are not used.
For starters, from the C area pointer with a separate size parameter was the only way to pass arrays around, due to array-to-pointer decay and lack of reference type.
In C++ there are alternatives to C arrays, like std::vector and std::array. But even when you have a (legacy) C array you have 2 situations:
if you pass it to a C function you don't have the option of reference, so you are stuck to pointer + size
when you want to pass it to a C++ function the idiomatic C++ way is to pass begin + end pointers.
First of all a begin + end iterators is generic, it accepts any kind of containers. But is not uncommon to see reference to std::vector when you want to avoid templates, so why not reference to C array if you have one? Because of a big drawback: you have to know the size of the array:
void foo(int (&arr)[5])
which is extremely limiting.
To get around this you need to make it a template:
template <std::size N>
void foo(int (&arr)[N])
which beats the purpose of avoiding templates, so you better go with begin + end template iterators instead.
In some cases (e.g. math calculations on just 2 or 3 values which have
the same semantics, so they shouldn't be separate parameters) a
specific array size is called for, and making the function generic
wouldn't make sense. In those cases, specifying the size of the array
guarantees safety since it only allows passing in an array of the
correct size at compile-time; therefore it's advantageous and isn't a
"big drawback"
One of the beauties of (C and) C++ is the enormous scope of applicability. So yes, you will always find some fields that use or need a certain unique feature in an unique way. That being said, even in your example I would still shy away from arrays. When you have a fixed number of values that shouldn't be semantically separated I think a structure would be the correct choice over arrays most of the time (e.g. glm::mat4 instead of float[4]).
But let's not forget what std::array is: a modern replacement for C arrays. One thing I learned when analyzing options is that there is no absolute "better than". There is always a "depends". But not in this case: std::array should unquestionably replace C arrays in interfaces. So in the rare case where a fixed size container is needed as a reference parameter it doesn't make sense to enable encouraging the use of C arrays when you already have an std::array. So the only valid case where exposing the underlying C array of std::array is need is for some old libraries that have C array reference parameters. But I think that in the bigger picture adding this to the interface it is not justified. New code should use a struct (btw std::tuple is getting easier and easier to use by each standard) or std::array.
AFAIK, There's no direct or typesafe way to do it, but one work around if you need to pass to a function (with a signature you cannot change to std::array) is by usingreinterpret_cast like this:
some_function(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data())));
If you wanted to make it safer:
#include <array>
void passarray(int (&myarr)[5]){}
template <typename ValueT, std::size_t size>
using CArray = ValueT[size];
template <typename ValueT, std::size_t size>
CArray<ValueT, size> & c_array_cast(std::array<ValueT, size> & arg) {
{
return *reinterpret_cast<CArray<ValueT,size>*>(arg.data());
}
int main()
{
std::array<int,5> myarr = { {1,2,3,4,5} };
passarray(*reinterpret_cast<int (*)[myarr.size()]>(myarr.data()));
passarray(c_array_cast(myarr));
return 0;
}
There isn't one.
I can see why it would be useful, especially when working with legacy code, but since a couple of decades ago we're supposed to be moving away from code like that and towards iterator-aware algorithms. And when working with C code you'd have to use a pointer anyway. I presume these are factors in the decision not to provide this functionality.
Rewrite your code to accept std::array<T, N>& instead, if possible.
You can reinterpret_cast the .data() to a raw, like:
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
But it is an ugly hack. As the others have already suggested, I recommend you to use std::array as-is.
Usage:
#include <cstdint>
#include <array>
template <typename T, std::size_t N>
inline static decltype(auto) to_raw_array(const std::array<T, N> & arr_v) {
return reinterpret_cast<const T(&) [N]>(*arr_v.data());
}
void foo(const std::uint8_t(&buf)[5]){
// ...
}
int main(void){
std::array<std::uint8_t, 5> arr = {1,2,3,4,5};
foo(to_raw_array(arr));
}
Why not passing std::array.begin()? Worked in SDL2 on:
int SDL_RenderDrawLines(SDL_Renderer *renderer, const SDL_Point *points, int count)
My line to be drawed:
std::array<SDL_Point, 8> a_line;
I passed like this:
SDL_RenderDrawLines(r_s_game.p_renderer, a_line.begin(), 8);
Simple question. I have a pointer to an array.
vector<int> myVector = { 22, 18, 12, -4, 58, 7, 31, 42 };
int* myPtr = myVector.data();
I also have a function that takes a reference to an array as a parameter.
template<typename T> void sort_quick(T (&arr)[]);
How can I pass my vector's array to this function without having to copy the potentially huge array in data().
sort_quick(*arr); // No matching function call for 'sort_quick'
Also, I need to pass it as an array, it's a pre-requisite, so don't come talking about just passing the vector because I wish I could.
Edit:
template<typename T, int N> void sort_quick(T (&arr)[N]);
This should now be legal syntax?
Edit2:
template<typename T> void sort_quick(T* arr, size_t length);
I believe this would be the best version then, when needing to deal with arrays and not vectors.
C-style array bounds must be known at compile-time. Your original definition of sort_quick was illegal. This is legal:
template<typename T, int N> void sort_quick(T (&arr)[N]);
however it can only be called with an actual array.
In order to support sorting containers whose sizes are not known until compile-time, you will need to make a version that takes two parameters. These could be two pointers, or a start pointer and a length. The version taking array could be made to delegate to the new version.
The idiomatic way would be to use iterators:
template<typename Iterator> void sort_quick(Iterator begin, Iterator end);
and then you can call the function like:
sort_quick(myVector.begin(), myVector.end());
The signature for your sorting function takes an array reference; Something you may not know is that no rvalue expression in C or C++ may yield an array, that is to say, an rvalue may not be of an array type. You may also not know about how arrays can decay in to pointers when passed as parameters to functions, but that is perhaps a little off topic;
Regardless, you may not do something like:
sort_quick(my_vec.data());
If you absolutely must use that function with that signature (i.e. it is an interface over which you have no control), you will need to construct an array using the information that the set of values is [my_vec.data(); my_vec.size()] is valid. As to why you can't just use std::vector::data(), if you look carefully, you will see it's return type is T*, and pointers will not magically reverse-decay in to array references.
If you don't use C++11 you can do it that way:
sort_quick( &arr[0] );
Why doesn't C++ provide us with a constructor which takes an array as an argument? Alternatively, is there anything wrong with defining the following function?
template <class T>
std::set<T> ArrayToSet(T array[]) {
return std::set<T>(array, array + sizeof(array) / sizeof(array[0]));
}
I think the answer might come down to ideas of dynamic memory allocation, but I'd like to hear some feedback on this.
Edit: I am not using C++11.
Alternatively, is there anything wrong with defining the following function?
There is: it doesn’t work.
You cannot pass variable-length C-style arrays to functions. The T array[] syntax in an argument list is a synonym for T* array: a raw pointer is passed, not an argument.
You can, however, pass fixed-sized arrays by reference (i.e. T (&array)[5] works). To make this work for different array lengths you need to use a non-type template argument:
template <class T, std::size_t N>
std::set<T> ArrayToSet(T (&array)[N]) {
return std::set<T>(array, array + N);
}
– But I agree in this with Zac: the function is over-specific (or, the other way round: not generic enough). There is already a universal collection-construction method, via iterators. So the correct way is to use C++11’s std::begin and std::end facility, and if you cannot use C++11, then the correct way is to write them yourself:
template <typename T, std::size_t N>
T* begin(T (&array)[N]) {
return array;
}
template <typename T, std::size_t N>
T* end(T (&array)[N]) {
return array + N;
}
Why doesn't C++ provide us with a constructor which takes an array as
an argument?
Why would it? A std::set is not an array, and it already has a constructor that takes iterators to initialize it, so adding another constructor for an array is unnecessary. std::vector IS an array and even it does not have a constructor that takes an array.
Alternatively, is there anything wrong with defining the following
function?
Yes and no. It is unnecessary as you can just write
MyType myArray[mySize];
std::set<MyType> mySet(myArray, myArray + sizeof(myArray) / sizeof(array[0]);
// or std::set<MyType> mySet(myArray, myArray + mySize);
// or std::set<MyType> mySet(std::begin(myArray), std::end(myArray)); c++11
It isn't really worthy of its own function.
If you really want to write a function to help you out, I'd approach it by porting std::begin and std::end to C++03. Those would at least be more usable than a function specifically to create a std::set.
It would look exactly like what Konrad posted in his answer.
std::set does not need to provide a constructor for C style arrays, because it is possible to construct from them already using iterators - furthermore it is not easy to construct from an array, because you could try to construct from T *, which does not convey the array length, or whether it's an array at all.
To do this, we use a template trick to determine the array size and use the iterator constructor:
template <typename T, size_t N>
std::set<T> ArrayToSet(T (& array)[N]) {
return std::set<T>(&array[0], &array[0]+N);
}
Note that, as stated in the comments, this will not work for T * types. You could overload for that giving an additional parameter arr_length or something. In this case sizeof doesn't work either, it would just give you the size of the pointer, not the array.
Some may or may not know that you can get the size of an array argument to a function using this code:
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
...
return;
}
where SIZE can be used to represent the number of elements in the array, allowing the, programmer using your function to pass the array as an argument without explicitly passing the length. For example, the programmer can do this:
SortingAlgorithm( arrayToBeSorted ); //the length is not passed here, which is fine
For Algorithms that can be implemented relatively easily in an iterative style, this is fine. But I've tried to do this with other algorithms that are recursive. The code for each one might look something like this:
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
DataType newArray[SIZE];
memcpy(newArray,array, SIZE); //copy to a new array
SortingAlgorithm( newArray );
...
return;
}
But this throws an error every time saying that the program expects a different argument type, type conversion has failed, and shows an attempt to typecast the SIZE specifier for the newArray array multiple times, and the program fails. It does not however spit out these errors if I use an actual value to define the size of newArray before making the recursive call, like this:
DataType newArray[10]; //arbitrary number, but the compiler accepts this.
Why is it that a variable sized array causes an error? Also is there anyway to implement a recursive algorithm that accepts an array as input but does not require the length of the array as an argument because It can determine the length of the array each time within the function call?
Make a helper function that takes a size, it can be used internally by your other function, and nobody has to know about it. For example:
template<typename DataType>
void SortingAlgorithm_helper(DataType * ptr, size_t size)
{
...
SortingAlgorithm_helper(ptr + 1, size - 1);
...
}
template<typename DataType, size_t SIZE>
void SortingAlgorithm(DataType (&array)[SIZE])
{
...
SortingAlgorithm_helper(newArray,SIZE);
...
}
By your comments, you are considering switching to vector. Well, you don't have to make a choice here. You can make the code much more generic to handle both. Instead of passing in a pointer and a size to the helper function, we pass in two iterators designating a range. Then we modify the main function to accept any container, as long as std::begin and std::end work on it.
template<typename Iterator>
void SortingAlgorithm_helper(Iterator first, Iterator last)
{
...
SortingAlgorithm(++first, last);
...
}
template<typename Container>
void SortingAlgorithm(Container & c)
{
...
SortingAlgorithm_helper(std::begin(c), std::end(c));
...
}
This should handle built-in arrays, std::vector, std::array and std::deque. If you limit the operations performed on the iterators to bi-directional (++ and --), then it should handle std::list as well.
Don't do this. Instead, use one of the following options:
Use a std::vector (it has a size() method) instead of an array.
Pass in the size of the array as an additional argument to your function.
As for recursive algorithms, don't just copy the elements into a new array, it's time consuming.
Instead, pass in two pointers to the beginning and the end of the portion of the array that you want to process (look at how STL algoritms work, std::sort doesn't take a container, instead it takes two iterators).
Note however, that this actually depends on the details of the algorithm you're using, some algorithms may actually require explicit copying, but you should avoid it when possible.
The template generates code at compile time, specifying a value like 10 allows it to generate code because it knows what value to use. Specifying something that can not be determined at compile time(like a variable that only has a value at run time) means it doesn't know what value to use when generating code.
The compiler needs to figure out the sizes at compile time, not run time. My compiler accepts your code as written, but apparently yours can't figure out the size. You can explicitly tell it the size like this:
SortingAlgorithm<DataType, SIZE>( newArray );.
Both versions of that line of code work with my compiler.