6

How do I make this work:

void foo(uint8_t a[]) { ... }

foo({0x01, 0x02, 0x03});

It gives me an error:

error: cannot convert '<brace-enclosed initializer list>' to 'uint8_t* {aka unsigned char*}' for argument '1'
                                                     ^
Timmmm
  • 88,195
  • 71
  • 364
  • 509

5 Answers5

9

This worked for me, I had to change the function signature but it's actually better in my case as it statically checks the array length:

void foo(std::array<uint8_t, 3> a) { /* use a.data() instead of a */ }

foo({0x01, 0x02, 0x03}); // OK

foo({0x01, 0x02}); // Works, at least on GCC 4.9.1. The third value is set to zero.

foo({0x01, 0x02, 0x03, 0x04}); // Compilation error.
Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • 1
    C-style arrays suck at *everything*. Always use std::array over them if possible. – Puppy Jul 21 '15 at 11:16
  • 1
    Maybe you can use a template like `template void foo(std::array a){/*...*/}`...(I haven't tried this way before..) – Shindou Jul 21 '15 at 11:19
  • 2
    @Shindou not in this case, `size` parameter cannot be inferred. – ForEveR Jul 21 '15 at 11:19
  • @ForEveR I've just tested. we have to call it like `foo({/*...*/});` to make it work – Shindou Jul 21 '15 at 11:25
  • @Shindou: Which is what he just said – Lightness Races in Orbit Jul 21 '15 at 11:26
  • So this is weird... now when I try this I don't get a compilation error for the wrong size array. Can anyone confirm whether they do or not, and whether you should. Could be a GCC bug (I'm using 4.9.1). – Timmmm Sep 10 '15 at 15:59
  • Actually after testing it seems that it allows shorter initialiser lists and fills them in with zeros, but longer ones cause an error. I may have lied in my original answer and have actually tested it with a longer list. – Timmmm Sep 10 '15 at 16:01
6

The answers so far haven't addressed the main problem with the question: In the signature

void foo(uint8_t a[])

a is not an array, but a pointer to a uint8_t. This is despite the fact that the declaration of a makes it look like an array. This is even pointed out by the error message:

cannot convert '<brace-enclosed initializer list>' to 'uint8_t* {aka unsigned char*}'

So, in the same way you are not allowed to do this:

uint8_t *a = {0x01, 0x02, 0x03}; // Eek! Error

You can't call foo({0x01, 0x02, 0x03}); With the signature above.

I suggest you take some time to read up on C-style arrays and how they are not first-class citizens in C++.

From the answer you posted to your own question, it seems that you are looking for a function that works for fixed-size arrays. But don't pass it by value! I recommend using the following declaration:

void foo(std::array<uint8_t, 3> const &a);
Community
  • 1
  • 1
Nasser Al-Shawwa
  • 3,573
  • 17
  • 27
4

You cannot. Just construct

uint8_t a[] = {0x01, 0x02, 0x03};

and call foo(a).

Or just use std::array, that is probably better.

ForEveR
  • 55,233
  • 2
  • 119
  • 133
3

This:

void foo(uint8_t a[]) { ... }

is a function that takes a uint8_t*, not an array - arrays are decayed to pointers when used as function arguments. The issue is that an initializer list (like {0x01, 0x02, 0x03}) cannot be converted to a uint8_t*.

If what you want is to pass an arbitrary number of uint8_ts to foo, the simple solution is to use the new std::initializer_list

void foo(std::initializer_list<uint8_t> a) { ... }

foo({0x01, 0x02, 0x03, 0x04, 0x05}); // OK - a has 5 elems in it

Or you could take a variadic pack and construct an array from it internally:

template <typename... Args,
          typename = std::enable_if_t<
              all_true<std::is_convertible<Args, uint8_t>::value...>
              >>
void foo(Args... elems) {
    uint8_t a[] = {elems...};
    // ...
}

That has slightly different usage:

foo({0x01, 0x02, 0x03}); // error
foo(0x01, 0x02, 0x03; // OK - a has 3 elems in it
Barry
  • 286,269
  • 29
  • 621
  • 977
0

foo(std::array<uint8_t, 3>{0x01, 0x02, 0x03}.data());

Bruno Martinez
  • 2,850
  • 2
  • 39
  • 47