10

I'm trying to call this method

#define SIZE 16
void DoSomething(char(&value)[SIZE])
{
}

From this method:

void BeforeDoingSomething(char* value, int len)
{
    if (len == SIZE)
    {
        DoSomething(value);
    }
}

Attempting to do this gives me this error:

a reference of type "char (&)[16]" (not const-qualified) cannot be initialized with a value of type "char *"

Any tips for how to get the compiler to accept value passed in the function BeforeDoingSomething?

user2357112
  • 260,549
  • 28
  • 431
  • 505
Zulukas
  • 1,180
  • 1
  • 15
  • 35
  • How sure are you that `value` points to the start of a `char[SIZE]`? You might be able to `reinterpret_cast` but I'm not certain. I wouldn't recommend in any case. – François Andrieux Sep 28 '18 at 17:45
  • I found a related question [here](https://stackoverflow.com/questions/20046429/casting-pointer-to-array-int-to-int2). While it might strictly speaking solve your problem, this feels like an awkward situation. I would encourage you to think about redesigning slightly. Why does `DoSomething` always need an array of length `SIZE`? Why can't `BeforeDoingSomething` always use an array of length `SIZE` instead of a pointer and length? – alter_igel Sep 28 '18 at 17:51
  • The reason for this awkward situation is because this is happening within a DLL. The developer who wrote `DoSomething` wrote it this way, and when a call is made into my DLL it's in the form inside of `BeforeDoingSomething`. I'm not sure how to even go about enforcing something like `char(&value)[SIZE]` when it comes to people calling into the DLL (which the callers will not be C++) – Zulukas Sep 28 '18 at 18:04

3 Answers3

6

As the error explains, you cannot initialize a reference to an array using a pointer.

If and only if you can prove that value does in fact point to (first element of) an array of appropriate type, then what you can do is explicitly reinterpret the pointer, and indirect it:

DoSomething(*std::launder(reinterpret_cast<char(*)[SIZE]>(value)));
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Is `std::launder` here because of the strict aliasing rule? – Quimby Sep 28 '18 at 18:11
  • 1
    @Quimby Exactly. – eerorika Sep 28 '18 at 18:11
  • I think that launder is mandatory here, it is needed in the case of char array as well. – geza Sep 28 '18 at 18:15
  • @user2079303 Thank you. – Quimby Sep 28 '18 at 18:17
  • @Quimby: Nope. It has nothing to do with strict aliasing rule. It is there to get a pointer which actually points to the char array (as `reinterpret_cast` doesn't do that for you, as the pointers are not interconvertible). – geza Sep 28 '18 at 18:19
  • @geza but why wouldn't it point to the array? I don't see any unions or const pointers there, so there isn't need for pointer redirection, right? So without the launder, the casted array pointer could decay into different location than the original pointer points to? – Quimby Sep 28 '18 at 18:29
  • @geza I think I remembered wrong, the round-trip conversion guarantee (which is what "no need to launder" was based on) applies to `unsigned char` only (and `std::byte`), not `char`. – eerorika Sep 28 '18 at 18:36
  • @Quimby: check out these questions: https://stackoverflow.com/questions/51552713/can-stdlaunder-be-used-to-convert-an-object-pointer-to-its-enclosing-array-poi https://stackoverflow.com/questions/47653305/is-there-a-semantic-difference-between-the-return-value-of-placement-new-and-t https://stackoverflow.com/questions/51258039/static-castd-pointer-value – geza Sep 28 '18 at 18:36
  • @user2079303: I think that launder is needed when the pointers are not pointer interconvertible (and pointer to the first element and pointer to the array are never pointer-interconvertible, not even for `byte[]` or `unsigned char[]`). Why do you think that it is not needed for `unsigned char`/`byte`? – geza Sep 28 '18 at 18:41
  • @geza Thank you, will look through it. – Quimby Sep 28 '18 at 18:43
  • @geza hmm. Maybe I've mixed up the aliasing exception of those types with guarantees of static casting `void`. I had the perception that converting an aliasing pointer back to type of original object was guaranteed to return the same value. But I can't find a rule supporting me, so I may well have been wrong. – eerorika Sep 28 '18 at 18:50
  • @user2079303: if the `char *` have been created from `char(*)[]` with `reinterpret_cast`, then I think you would be right. But in this case, it was (supposedly) decayed (no `reinterpret_cast`, but a "fresh" new pointer created to the first element), and then it was `reinterpret_cast`'d back. This is a different case, that's why launder is needed. C++ is hard :) – geza Sep 28 '18 at 18:55
3

You can do it like this, but only in the case if there is a char[16] at the address where value points:

DoSomething(*std::launder(reinterpret_cast<char (*)[16]>(value)));

It is the same case as the first example here.

geza
  • 28,403
  • 6
  • 61
  • 135
3

The use of std::launder as suggested by @user2079303 is a good option if you are sure that value does indeed point to an array of the right size as mentioned in their answer.

Yet another approach: since SIZE is fairly small in this case, it may be safer/simpler to create a temporary copy of value and pass it to DoSomething(). But it all depends on what DoSomething() is actually doing (for example, does it modify the array passed to it). For example:

#include <iostream>
#include <vector>
#include <string>

constexpr int SIZE = 16 ;

void DoSomething(char (&value)[SIZE])
{
    std::cout << "Do it!" << std::endl ;
}

void BeforeDoingSomething(char* value, int len)
{
    if (len == SIZE)
    {
        char tmp_array[SIZE] ;
        std::copy(value, value + len, tmp_array) ;
        DoSomething(tmp_array);
    } else {
        std::cout << "Length: " << len << std::endl ;   
    }
}

int main()
{
    std::string foo (SIZE, '-') ;
    BeforeDoingSomething(foo.data(), foo.size()) ;

    std::vector<char> bar (SIZE) ;
    BeforeDoingSomething(bar.data(), bar.size()) ;

    std::string qux ;
    BeforeDoingSomething(qux.data(), qux.size()) ;

    return 0 ;   
}

Try it online here.

crayzeewulf
  • 5,840
  • 1
  • 27
  • 30