2

I have an existing C++ API function that looks like this:

void do_something(const Foo (&input_array)[N]);

I think that this takes an array of N contiguously allocated Foo structures, by reference. For example, I can pass in an array:

Foo my_array[N] = { ... }
do_something(my_array);

In my real code however I have a std::vector<Foo>, the contents of which I understand can be treated like an array using the .data() member function, however the following results in a compiler (clang) error:

// Say N is 2:
std::vector<Foo> my_vector(N);
// ...initialise vector values...
do_something(*my_vector.data());

It complains that there's no viable conversion from const Foo to const Foo [2].

I understand that C++ considers array sizes to be considered part of the type. In this case, what would be the correct type cast? I got as far as this:

  auto my_vector_as_array = static_cast<const Foo & [N]>(*my_vector.data());

However the compiler is not happy at all about the use of [N] or even [] as part of the static cast's type parameter, complaining that:

error: 'type name' declared as array of references of type 'const Foo &'

Is there a way to correctly coerce the contents of a std::vector into this function?

Here's the full code:

#include <iostream>
#include <vector>
#define N 4

struct Foo { int a; int b; float c; };

void do_something(const Foo (&input_array)[N]) {
  for (size_t i = 0; i < N; ++i) {
    std::cout << i << ": " << input_array[i].a << ", " << input_array[i].b << ", " << input_array[i].c << std::endl;
  }
}

int main(int argc, char * argv[]) {
  // pass a C-style array by reference:
  Foo my_array[N] = { {1, 10, 100.0}, {2, 20, 200.0}, {3, 30, 300.0}, {4, 40, 400.0} };
  do_something(my_array);

  // try to do the same with the contents of a vector:
  std::vector<Foo> my_vector(N);
  my_vector[0] = {1, 10, 100.0};
  my_vector[1] = {2, 20, 200.0};
  my_vector[2] = {3, 30, 300.0};
  my_vector[3] = {4, 40, 400.0};

  // this fails to compile:
  do_something(*my_vector.data());

  // try casting via a temporary variable - also fails to compile:
  auto my_vector_as_array = static_cast<const Foo & []>(*my_vector.data());
  do_something(my_vector_as_array);
}

EDIT: it has been suggested that this is a duplicate of this question however the solutions presented there do not work in this case. I think this may be because the function defines the array reference with an explicit size, and the compiler is not happy accepting a mere pointer (because the size is lost from the type).

davidA
  • 12,528
  • 9
  • 64
  • 96
  • 1
    OT: in C++, you don't need to use `struct Foo`; you can just use `Foo` – Justin Aug 14 '17 at 02:49
  • Possible duplicate of [How to use a std::vector in a C function](https://stackoverflow.com/questions/6701816/how-to-use-a-stdvector-in-a-c-function) – Andy Aug 14 '17 at 02:55
  • @Justin ah, thanks. I am actually using a struct typedef in my actual code but threw the `struct` into my example out of (bad) habit. – davidA Aug 14 '17 at 02:55
  • @Andy I tried `&my_vector[0]` as per that link however it doesn't work - still complains about no known conversion between 'value_type *' and 'const struct Foo [4]'. I wonder if a pointer type is too general for automatic conversion and I need an explicit cast. – davidA Aug 14 '17 at 02:58
  • 3
    @Andy The OP isn't trying to pass a `std::vector` to a C function; he's trying to pass it to a C++ function that accepts a completely C++ argument type. To be honest, nothing in the *problem* within this question has anything to do with C per-se, and including its various references in the question adds more to confusion more than it does clarity. – WhozCraig Aug 14 '17 at 02:59
  • @WhozCraig good point, I'll remove the "C" references. – davidA Aug 14 '17 at 03:02
  • Ultimately, you're asking if there is any legal way to produce a conversion from `std::vector` to `Type (&)[N]`, unless i'm missing something, and if that is the case, I cannot think of a legal way to do it without violating aliasing. There are *plenty* of language-savvy people on this site smarter than I (believe me) that may come up with something, so don't take my conclusion as gospel. Let it gel for a bit. – WhozCraig Aug 14 '17 at 03:03
  • @WhozCraig yes, I think that is what I am asking. The vector storage (for non-bool) is supposedly contiguous so the array exists, it just needs the right type cast. If it's possible I'd like to avoid a `reinterpret_cast` though. I will wait patiently for any expert that may advise... – davidA Aug 14 '17 at 03:07
  • 2
    @WhozCraig I think that the problem can be reduced to finding a conversion from `Type *` to `Type (&)[N]`, which sounds like it should be doable – Justin Aug 14 '17 at 03:15
  • Another answer suggests that using a pointer to an array reference (and then dereferencing it to pass the array) may be a possible strategy, however clang tells me that a static_cast from 'Foo * ' to 'Foo (*)[4]' is "not allowed": `auto c = static_cast(my_vector.data());` https://stackoverflow.com/a/20046703/143397 – davidA Aug 14 '17 at 03:22
  • I suppose there is no (clean) way to do it. Pointer type inherently has less information than array type. So the direction of conversion is one-way only. –  Aug 14 '17 at 03:22
  • Also, `std::vector` manages raw memory and then treat part of the raw memory as array. `new[]` returns a pointer to the element type. Both cases do not involve array type and only use pointer type. –  Aug 14 '17 at 03:23
  • @NickyC I think you're probably right - the closest thing I can find is the following discussion that uses a static cast via `void *` and is ugly: https://stackoverflow.com/a/2634994/143397 – davidA Aug 14 '17 at 03:26
  • Afaik, std::vector doesn't use new[], but a series of placement news, so there isn't a genuine array object. I wonder if this makes any difference for alias analysis. – MikeMB Aug 14 '17 at 05:45

1 Answers1

2

One solution I've found via here is to convert via a void * cast:

Foo (&c)[N] = *static_cast<Foo(*)[N]>(static_cast<void*>(my_vector.data()));
do_something(c);                                           

This seems to compile and execute correctly, and is probably unsafe unless one can guarantee that the value of N used by the function is identical to the size of the vector.

davidA
  • 12,528
  • 9
  • 64
  • 96