use std::initialiser_list to explicitly initialise a variable - c++

this code
std::initializer_list<const char*> list {"something", "somthingElse" /*..*/};
const char* array[] = list;
fails to compile with the following error on error:
array initializer must be an initializer list
Can't really understand what I'm doing wrong here since I'm using an initializer_list after all.
(The reason I use an initializer_list is so I can use list.size() later in my code in several parts; it'd be error_prone having to adjust a series of magic constants each time I add/remove something from the list)

To initialize an array, you need a brace-enclosed initializer list which is not the same as a std::initializer_list.
To get what you're trying to achieve, you could use a std::array, but you'll need a helper function to deduce its size parameter:
#include <array>
template<typename T, typename... Ts>
constexpr std::array<T, sizeof...(Ts)> make_array(Ts... i)
{
return {i...};
}
int main() {
auto a = make_array<const char*>( "a", "b", "c", "d" );
return a.size(); // I get an exit value of 4 here
}

Related

C++ pass an array to a function without knowing its type

I want to make a function that takes an array as an argument with ANY type and returns its length like the following example:
unsigned int getLength(array)
{
return sizeof(array) / sizeof(array[0])
}
I don't know if it's possible to even possible to pass an array without knowing its type, I hope I explained my question well, thank you...
You can use templates as shown below. In particular, we can make use of template nontype parameter N to represent the length of the passed array and template type parameter T to represent the type of the element inside the array.
template<typename T, std::size_t N>
//--------------------------v--------------->type of elements inside array named arr
std::size_t getLength(const T (&arr)[N])
//-----------------------------------^------>length of the array
{
return N ;
}
int main()
{
int arr[] = {1,2,3};
std::cout<<getLength(arr)<<std::endl;
std::string arr2[] = {"a", "b"};
std::cout<<getLength(arr2)<<std::endl;
return 0;
}
Working demo
Note with C++17 there is also an option to use std::size.

Function template which returns a function parameter inside an array

I'm trying to create a function template which returns the given parameter contained within an array of its own type:
template<typename T>
auto encArr(T obj){T arr[1] = {obj};} // For instance - if obj is an integer it should return
// int arr[1] = {obj};
// In general, it should return
// T arr[1] = {obj};
But when I try to use the function my compiler fights back:
int test = 10;
int test[1] = encArr(huh); // error: array must be initialized with a brace-enclosed initializer
Perhaps I'm just going about this in the wrong way. It is quite important however that I'm not using a std::vector<>, I would like to use a standard C++ array. Thank you very much for any help!
First off, your example is missing a return statement.
But, in any case, you simply can't return a C-style flat array. But you can return a C++-style std::array instead, if you don't want to use std::vector.
#include <array>
template<typename T>
auto encArr(const T &obj){
return std::array<T, 1>{obj};
}
auto test = encArr(10);
// use test[0] as needed...
Online Demo

Constexpr: Convert a list of string views into a list of char arrays

I came across an interesting constexpr problem that haven't been able to fully crack. The last piece of the puzzle that I'm missing is the following
// Given a constexpr array of string views
constexpr std::array<std::string_view, X> views = ...;
// Convert it to a list of char arrays
constexpr std::tuple<std::array<char, Xs>...> buffers = ...;
My problem here is finding the right size for each array. How can I extract the sizes of the string_views in views and pass them as template parameters to another function?
I could just use the same size for every buffer, big enough to hold every argument, but I'm wondering if there is a way of optimizing their sizes since the info is known at compile time.
Full description of the problem I'm trying to solve.
(in case someone can come up with a better approach, also cause I think it's interesting...)
I want to create a macro that converts a list of arguments into a list of name-value pairs. For instance
// Adding types explicitly to show what I want to achieve.
int x;
float y;
double z;
using named_values_t = std::tuple<
std::pair<const char*, int&>,
std::pair<const char*, float&>,
std::pair<const char*, double&>>;
named_values_t nv = MY_MACRO(x, y, z);
The const char* adds significant difficulty but it's a requirement for a third-party lib.
Now I know that can be done with Boost.Preprocessor but I'd like to do it just using the STL and constexpr methods to avoid adding boost just for this. Also I know this would be trivial on a compiler with support for constexpr std::string but I'm using C++17.
The string processing can be easily done with constexpr functions,
// Split "one, two, three" into {"one", "two", "three"}.
template <size_t Count>
constexpr std::array<std::string_view, Count> split_arguments(std::string_view);
However, I cannot pass these string_views directly as char pointers, since at this point they're just pointers to a bigger array in memory (the complete "one, two, three"). To pass as const char* each element needs to be null-terminated.
But we can build an std::array for each std::string_view and copy its contents, this way we have a char array for each argument name, that will generate a null-terminated segment of memory for each name.
constexpr std::string_view args = "one, two, three";
constexpr std::array<std::string_view, 3> views = split_arguments<3>(args); // {"one", "two", "three"}
constexpr std::tuple<std::array<char, Xs>...> buffers = make_buffers<Xs...>(views);
Here I'm not able to figure out how pass the lengths of the views as template arguments to the next function.
Working solution here (using bigger fixed-sized buffers): https://gcc.godbolt.org/z/WKsbvb
The fixed-size buffer solution is ok, but it would be great to go that extra step to fit the buffers to their actual size.
So long as views is a variable with static storage duration (rather than a prvalue created by a constexpr function call), you can implement this with the usual auto& template parameter and std::index_sequence tricks:
#include<array>
#include<string_view>
#include<tuple>
#include<utility>
#include<type_traits>
#include<cstddef>
namespace detail {
template<std::size_t N>
constexpr auto copy_string(std::string_view s) {
std::array<char,N+1> ret{}; // zero-initialize
for(std::size_t i=N;i--;) ret[i]=s[i];
return ret;
}
template<auto &V,std::size_t ...II>
constexpr auto buffers(std::index_sequence<II...>) {
return std::make_tuple(copy_string<V[II].size()>(V[II])...);
}
}
template<auto &V> constexpr auto buffers=
detail::buffers<V>
(std::make_index_sequence
<std::tuple_size_v<std::remove_reference_t<decltype(V)>>>());
constexpr std::array<std::string_view, 3> views = {"C","++",""};
static_assert(std::is_same_v
<decltype(buffers<views>),
const std::tuple<std::array<char,2>,
std::array<char,3>,
std::array<char,1>>>);
static_assert(std::get<0>(buffers<views>)[0]=='C');
static_assert(std::get<1>(buffers<views>)[1]=='+');
static_assert(std::get<2>(buffers<views>)[0]=='\0');

Error : cannot convert ‘std::vector<float>’ to ‘float’ in initialization

I have defined a vector of the boundary_info structure as std::vector<boundary_info> nodes to be used in my code for a specific purpose. While I try to push_back new elements into this vector in a specific function as:
void myFun()
{
std::vector<float_type> dists(9, -1.0);
std::array<float_type,9> f, g;
//do something - x and y are defined here
nodes.push_back(boundary_info{point<int>{x,y}, dists, f, g, {}});
}
I get the following error message :
Error 1 : cannot convert ‘std::vector<float>’ to ‘float’ in initialization
Error 2 : cannot convert ‘std::array<float, 9ul>’ to ‘float’ in
initialization
Error 3 : cannot convert ‘std::array<float, 9ul>’ to ‘float’ in
initialization
Error 1 is associated with dists, which is a vector. Errors 2 and 3 are associated with the f, g passed as parameters in push_back respectively.
The code is shown below.
#include <iostream>
#include <vector>
template <typename T>
struct point //specify a point structure
{
T x,y;
};
struct boundary_info
{
point<int> xy_bdary; //coordinates of a bdary point
std::array<float_type,9> dist; //distance from boundary
std::array<float_type,9> f_prev, g_prev; //populations
std::vector<int> miss_dirns; //missing directions
};
I would be glad if the solution for this error would be pointed out. I have been struggling with it since half a day.
Note : I am compiling using c++11.
Edit
You can find a minimal code of this problem reproducing the same problem at
https://repl.it/repls/GleefulTartMarkuplanguage
Thanks
In the following line you are trying to initialize a std::array (boundary_info::dist) from a std::vector (dists):
nodes.push_back(boundary_info{point<int>{x,y}, dists, f, g, {}});
std::array doesn't have a constructor that accepts a std::vector. You could only initialize the std::array element-wise (aggregate initialization) or explicitly copy the std::vector to the std::array.
Aggregate initialization
nodes.push_back(boundary_info{point<int>{x,y}, {dists[0], dists[1], dists[2], dists[3], dists[4], dists[5], dists[6], dists[7], dists[8]}, f, g, {}});
Of course, that's not very elegant.
Copy std::vector to std::array
With the help of a little template function, we can do better.
template<typename T, std::size_t N, typename Range>
std::array<T,N> to_array( Range const& in )
{
std::array<T,N> result;
// To make the standard begin() and end() in addition to any user-defined
// overloads available for ADL.
using std::begin; using std::end;
std::copy( begin( in ), end( in ), result.begin() );
return result;
}
Live demo
to_array accepts any input type that has begin() and end() member functions or overloads of the free functions begin() and end().
Now you can initialize the array from the vector like this:
nodes.push_back(boundary_info{point<int>{x,y}, to_array<float_type,9>(dists), f, g, {}});
Note that you can easily shoot yourself in the foot if dists has more elements than the array, because to_array doesn't do any range checking (std::copy doesn't do either). I'll leave it as an exercise for the reader to make the function more secure, if needed.

Initialization of all elements of an array to one default value in C++?

C++ Notes: Array Initialization has a nice list over initialization of arrays. I have a
int array[100] = {-1};
expecting it to be full with -1's but its not, only first value is and the rest are 0's mixed with random values.
The code
int array[100] = {0};
works just fine and sets each element to 0.
What am I missing here.. Can't one initialize it if the value isn't zero ?
And 2: Is the default initialization (as above) faster than the usual loop through the whole array and assign a value or does it do the same thing?
Using the syntax that you used,
int array[100] = {-1};
says "set the first element to -1 and the rest to 0" since all omitted elements are set to 0.
In C++, to set them all to -1, you can use something like std::fill_n (from <algorithm>):
std::fill_n(array, 100, -1);
In portable C, you have to roll your own loop. There are compiler-extensions or you can depend on implementation-defined behavior as a shortcut if that's acceptable.
There is an extension to the gcc compiler which allows the syntax:
int array[100] = { [0 ... 99] = -1 };
This would set all of the elements to -1.
This is known as "Designated Initializers" see here for further information.
Note this isn't implemented for the gcc c++ compiler.
The page you linked to already gave the answer to the first part:
If an explicit array size is
specified, but an shorter
initiliazation list is specified, the
unspecified elements are set to zero.
There is no built-in way to initialize the entire array to some non-zero value.
As for which is faster, the usual rule applies: "The method that gives the compiler the most freedom is probably faster".
int array[100] = {0};
simply tells the compiler "set these 100 ints to zero", which the compiler can optimize freely.
for (int i = 0; i < 100; ++i){
array[i] = 0;
}
is a lot more specific. It tells the compiler to create an iteration variable i, it tells it the order in which the elements should be initialized, and so on. Of course, the compiler is likely to optimize that away, but the point is that here you are overspecifying the problem, forcing the compiler to work harder to get to the same result.
Finally, if you want to set the array to a non-zero value, you should (in C++, at least) use std::fill:
std::fill(array, array+100, 42); // sets every value in the array to 42
Again, you could do the same with an array, but this is more concise, and gives the compiler more freedom. You're just saying that you want the entire array filled with the value 42. You don't say anything about in which order it should be done, or anything else.
C++11 has another (imperfect) option:
std::array<int, 100> a;
a.fill(-1);
With {} you assign the elements as they are declared; the rest is initialized with 0.
If there is no = {} to initalize, the content is undefined.
Using std::array, we can do this in a fairly straightforward way in C++14. It is possible to do in C++11 only, but slightly more complicated.
Our interface is a compile-time size and a default value.
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
The third function is mainly for convenience, so the user does not have to construct a std::integral_constant<std::size_t, size> themselves, as that is a pretty wordy construction. The real work is done by one of the first two functions.
The first overload is pretty straightforward: It constructs a std::array of size 0. There is no copying necessary, we just construct it.
The second overload is a little trickier. It forwards along the value it got as the source, and it also constructs an instance of make_index_sequence and just calls some other implementation function. What does that function look like?
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
This constructs the first size - 1 arguments by copying the value we passed in. Here, we use our variadic parameter pack indexes just as something to expand. There are size - 1 entries in that pack (as we specified in the construction of make_index_sequence), and they have values of 0, 1, 2, 3, ..., size - 2. However, we do not care about the values (so we cast it to void, to silence any compiler warnings). Parameter pack expansion expands out our code to something like this (assuming size == 4):
return std::array<std::decay_t<T>, 4>{ (static_cast<void>(0), value), (static_cast<void>(1), value), (static_cast<void>(2), value), std::forward<T>(value) };
We use those parentheses to ensure that the variadic pack expansion ... expands what we want, and also to ensure we are using the comma operator. Without the parentheses, it would look like we are passing a bunch of arguments to our array initialization, but really, we are evaluating the index, casting it to void, ignoring that void result, and then returning value, which is copied into the array.
The final argument, the one we call std::forward on, is a minor optimization. If someone passes in a temporary std::string and says "make an array of 5 of these", we would like to have 4 copies and 1 move, instead of 5 copies. The std::forward ensures that we do this.
The full code, including headers and some unit tests:
#include <array>
#include <type_traits>
#include <utility>
namespace detail {
template<std::size_t size, typename T, std::size_t... indexes>
constexpr auto make_array_n_impl(T && value, std::index_sequence<indexes...>) {
// Use the comma operator to expand the variadic pack
// Move the last element in if possible. Order of evaluation is well-defined
// for aggregate initialization, so there is no risk of copy-after-move
return std::array<std::decay_t<T>, size>{ (static_cast<void>(indexes), value)..., std::forward<T>(value) };
}
} // namespace detail
template<typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, 0>, T &&) {
return std::array<std::decay_t<T>, 0>{};
}
template<std::size_t size, typename T>
constexpr auto make_array_n(std::integral_constant<std::size_t, size>, T && value) {
return detail::make_array_n_impl<size>(std::forward<T>(value), std::make_index_sequence<size - 1>{});
}
template<std::size_t size, typename T>
constexpr auto make_array_n(T && value) {
return make_array_n(std::integral_constant<std::size_t, size>{}, std::forward<T>(value));
}
struct non_copyable {
constexpr non_copyable() = default;
constexpr non_copyable(non_copyable const &) = delete;
constexpr non_copyable(non_copyable &&) = default;
};
int main() {
constexpr auto array_n = make_array_n<6>(5);
static_assert(std::is_same<std::decay_t<decltype(array_n)>::value_type, int>::value, "Incorrect type from make_array_n.");
static_assert(array_n.size() == 6, "Incorrect size from make_array_n.");
static_assert(array_n[3] == 5, "Incorrect values from make_array_n.");
constexpr auto array_non_copyable = make_array_n<1>(non_copyable{});
static_assert(array_non_copyable.size() == 1, "Incorrect array size of 1 for move-only types.");
constexpr auto array_empty = make_array_n<0>(2);
static_assert(array_empty.empty(), "Incorrect array size for empty array.");
constexpr auto array_non_copyable_empty = make_array_n<0>(non_copyable{});
static_assert(array_non_copyable_empty.empty(), "Incorrect array size for empty array of move-only.");
}
The page you linked states
If an explicit array size is specified, but an shorter initiliazation list is specified, the unspecified elements are set to zero.
Speed issue: Any differences would be negligible for arrays this small. If you work with large arrays and speed is much more important than size, you can have a const array of the default values (initialized at compile time) and then memcpy them to the modifiable array.
Another way of initializing the array to a common value, would be to actually generate the list of elements in a series of defines:
#define DUP1( X ) ( X )
#define DUP2( X ) DUP1( X ), ( X )
#define DUP3( X ) DUP2( X ), ( X )
#define DUP4( X ) DUP3( X ), ( X )
#define DUP5( X ) DUP4( X ), ( X )
.
.
#define DUP100( X ) DUP99( X ), ( X )
#define DUPx( X, N ) DUP##N( X )
#define DUP( X, N ) DUPx( X, N )
Initializing an array to a common value can easily be done:
#define LIST_MAX 6
static unsigned char List[ LIST_MAX ]= { DUP( 123, LIST_MAX ) };
Note: DUPx introduced to enable macro substitution in parameters to DUP
For the case of an array of single-byte elements, you can use memset to set all elements to the same value.
There's an example here.
The simplest way is to use std::array and write a function template that will return the required std::array with all of its element initialized with the passed argument as shown below.
C++11 Version
template<std::size_t N> std::array<int, N> make_array(int val)
{
std::array<int, N> tempArray{};
for(int &elem:tempArray)
{
elem = val;
}
return tempArray;
}
int main()
{
//---------------------V-------->number of elements
auto arr = make_array<8>(5);
//------------------------^---->value of element to be initialized with
//lets confirm if all objects have the expected value
for(const auto &elem: arr)
{
std::cout << elem << std::endl; //prints all 5
}
}
Working demo
C++17 Version
With C++17 you can add constexpr to the function template so that it can be used in constexpr context:
//-----------------------------------------vvvvvvvvv--->added constexpr
template<std::size_t N> std::array<int, N> constexpr make_array(int val)
{
std::array<int, N> tempArray{};
for(int &elem:tempArray)
{
elem = val;
}
return tempArray;
}
int main()
{
//--vvvvvvvvv------------------------------>constexpr added
constexpr auto arr = make_array<8>(5);
for(const auto &elem: arr)
{
std::cout << elem << std::endl;
}
}
Working demo
1) When you use an initializer, for a struct or an array like that, the unspecified values are essentially default constructed. In the case of a primitive type like ints, that means they will be zeroed. Note that this applies recursively: you could have an array of structs containing arrays and if you specify just the first field of the first struct, then all the rest will be initialized with zeros and default constructors.
2) The compiler will probably generate initializer code that is at least as good as you could do by hand. I tend to prefer to let the compiler do the initialization for me, when possible.
In the C++ programming language V4, Stroustrup recommends using vectors or valarrays over builtin arrays. With valarrary's, when you create them, you can init them to a specific value like:
valarray <int>seven7s=(7777777,7);
To initialize an array 7 members long with "7777777".
This is a C++ way of implementing the answer using a C++ data structure instead of a "plain old C" array.
I switched to using the valarray as an attempt in my code to try to use C++'isms v. C'isms....