5

I have a variable of type Blah.

I want to cast it to char[sizeof(blah)], without copying.
I need the type cast to be strong enough to instantiate a template that expects char[N].

I've tried many things, but i can't quite get it.
I want something like this to work correctly:

class Blah {
 int a;   
};


template <typename T>
void foo (T& a) 
{ 
    //Not an array
}

template <int N>
void foo (char(&a)[N]) 
{ 
    //an array!
}

Blah b;
foo(b); //not an array
foo((char[sizeofBlah])b); //hopefully treated as an array
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185

3 Answers3

10

You can’t perform such a cast, that doesn’t make sense. What you can do is get the address of the object and reinterpret the address as a byte address:

char* const buf = reinterpret_cast<char*>(&obj);

That should fulfil your requirements, but beware of using the terminology “cast to char[]” because it obfuscates the actual operation that is taking place.

You can also interpret the address as the starting address of a fixed-sized buffer, of course:

using buffer_t = char[sizeof(Blah)];
buffer_t* pbuf = reinterpret_cast<buffer_t*>(&obj);

But notice that you are still using a pointer to the buffer here.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    It would make sense if you want to treat the struct as an array, and cast it as such (for serialization in example). – Yochai Timmer Mar 19 '13 at 16:29
  • 2
    @YochaiTimmer Again, I disagree. You are not (only) performing a cast. However, see my updated answer. – Konrad Rudolph Mar 19 '13 at 16:31
  • @YochaiTimmer, it works until your struct contains only integral types. For pointers/complex objects, it wouldn't work. Therefore, use of `reinterpret_cast` for serialization is a way to disaster. – hate-engine Mar 19 '13 at 16:35
  • @hate-engine I know that, this is intended for simple structs. – Yochai Timmer Mar 19 '13 at 16:44
6

You can do this with reinterpret_cast<char (&)[sizeof b]>(b), but I do not recommend it.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • sizeof Blah ... the type ... thanks, that what i was looking for. – Yochai Timmer Mar 19 '13 at 16:45
  • If the compiler likes it, it's legal. And yea the cast works good enough, and the code works as intended. – Yochai Timmer Mar 19 '13 at 16:48
  • I tried casting to an array reference, but kept getting compiler errors because i didn't put () around the &. – Yochai Timmer Mar 19 '13 at 16:49
  • 7
    @Yochai You seem to have no idea how C++ works. Lots of illegal code compiles. “works as intended” – maybe on your specific compiler / computer combination. That’s not enough for robust software. – Konrad Rudolph Mar 19 '13 at 16:49
  • 3
    @Eric: In terms of pointers *to* the base type of that reference. That means `char (*)[N]`. And *that* pointer type is not allowed to alias anything at all. What you're probably thinking is `char*`, which can alias anything. As far as I can see, you're invoking undefined behaviour. – Xeo Mar 19 '13 at 16:54
  • @Eric That’s not a variable-length array, it’s fixed-length since `sizeof b` is a compile-time constant. VLAs are *not* standard-compliant. – Konrad Rudolph Mar 19 '13 at 16:58
  • @KonradRudolph I have a good knowledge on how the compiler works thank you very much. And i know this is legal as just as i knew there should be away to do this because it should be supported in the language. A simplistic comment doesn't mean lack of knowledge. Any class or struct IS an array of bytes, and so it would make sense to have this capability without casting to char pointer. – Yochai Timmer Mar 19 '13 at 17:30
  • @Xeo Passing arrays by reference is well defined. It is used in many cases, usually combined with templates to recover the template size, as is shown here: http://stackoverflow.com/questions/968001/determine-size-of-array-if-passed-to-function – Yochai Timmer Mar 19 '13 at 17:36
  • I think Xeo and Konrad Rudolph are correct, converting a pointer to `Blah` to a pointer to an array of `char` is not defined by the C++ standard. Nobody should rely on this code without further specification from their compiler. – Eric Postpischil Mar 19 '13 at 17:38
  • @YochiaTimmer: The issue is not a pass by reference, it is a conversion. – Eric Postpischil Mar 19 '13 at 17:39
  • I think it is defined quite well: "reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type." – Yochai Timmer Mar 19 '13 at 17:42
  • 1
    @YochaiTimmer: That text appears to be from [here](http://en.cppreference.com/w/cpp/language/reinterpret_cast). That web site does not define C++. The ISO/IEC 14882 C++ standard defines C++. – Eric Postpischil Mar 19 '13 at 17:54
  • @EricPostpischil That site is usually accurate, and simplifies the jargon of the ISO. But if you must have the ISO quotation: – Yochai Timmer Mar 20 '13 at 05:07
  • 1
    An lvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. That is, a reference cast reinterpret_cast(x) has the same effect as the conversion *reinterpret_cast(&x) with the built-in & and * operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type. No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called. – Yochai Timmer Mar 20 '13 at 05:08
  • Which makes it pretty much equivalent to what Konrad did when casting it to char*. – Yochai Timmer Mar 20 '13 at 05:09
  • @YochaiTimmer: Now you need to show that a conversion from pointer to `Blah` to pointer to array of `char` is defined. – Eric Postpischil Mar 20 '13 at 09:44
  • @EricPostpischil lol, tough crowd: Section 3.9, Types: `For any complete POD object type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.` – Yochai Timmer Mar 20 '13 at 14:10
  • `The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T.` – Yochai Timmer Mar 20 '13 at 14:11
  • @YochiaTimmer: The fact that you can copy the bytes into an array of `char` does not speak to whether a pointer to the object can be converted to a pointer to an array of char. – Eric Postpischil Mar 20 '13 at 14:19
1

The cleanest way would be to add it as an operation into the class:

class Blah {
    int a;
public:
    void serialize(char *output) { output[0] = a; /* add others as needed */ }
};

Blah blah;
char buffer[sizeof(Blah)];
blah.serialize(buffer);

This will allow you to explicitly see what's going on and centralize the code in case you need to change it later.

Edit: The serialize interface is not very elegant (or very safe) in my example, but my point is that you should add it as a method.

utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • The Blah class should be any class, not something i wrote. i can't add methods to it. But in general i'd agree with this approach. – Yochai Timmer Mar 19 '13 at 16:43
  • If it's not performance-critical code, the cleanest way would be to add iostream `operator<<` and `operator>>` implementations for your class. This would have the advantage of giving you a whole range of functionality for free (`boost::lexical_cast` would work for you, as would i/o using `std::istream_iterator` and `std::ostream_iterator`). – utnapistim Mar 19 '13 at 19:45