I come from .NET programming using VB.NET and this is my first C development for an Arduino based application.

Reading through the Arduino header files (looking for something like a .ToString method for the __FlashStringHelper) I stumbled upon the next #define

#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))

First what I understand:

This is a macro with a parameter named string_literal and this parameter is somehow used in the expression inside parenthesis. That's all!

What are these <, > signs and that * pointer sign all of a sudden?

有帮助吗?

解决方案

General type casting
In C++, there are various types of casts which can be used to convert between types. They are important because C++ is a strongly typed language, and the compiler doesn't necessarily know that any given conversion between types is safe or sensible. By default, it will issue an error or a warning unless you explicitly tell it what to do.

All of the C++ casts have the following format:

x_cast <new_type> (expression)

x_cast can be one of the following: static_cast, dynamic_cast, reinterpret_cast, or const_cast.

A reinterpret cast is used when you want to force the compiler to convert between unrelated pointer types. The result is a pointer to exactly the same piece of data in memory. However, it will be handled (or interpreted) as though it's a different type, which can allow some interesting operations.

For example, let's say you have a pointer to a 4 byte unsigned integer, and you want to access each byte individually. You could do that by reinterpreting the pointer to a 1 byte type, like this:

uint32_t num = 12345;
uint32_t *p1 = &num;

uint8_t *p2 = reinterpret_cast<uint8_t*>(p1);

// Access the individual bytes:
uint8_t byte0 = p2[0];
uint8_t byte1 = p2[1];
uint8_t byte2 = p2[2];
uint8_t byte3 = p2[3];

Pointers p1 and p2 both point to the data stored in the num variable. The difference is that accessing it via p2 will result in the compiler treating it like a 1 byte unsigned integer (instead of the original 4 byte type). This lets you extract/manipulate individual bytes at different locations within the original variable.

For a simple example like this, reinterpret_cast is quite safe. However, there are many situations where it can go very badly wrong, or simply do nothing useful, if it's not used carefully. An example would be trying to reinterpret a float pointer as an int. On its own, it won't do anything bad. The result will be completely useless though, because the underlying binary representation of a float doesn't make sense if you try to handle it like an int.

The same approach can work for objects, letting you interpret an instance of one class as though it's an instance of a different one. However, it doesn't do any intelligent conversion. It simply forces the raw binary data to be treated in a different way, which means you have to be very confident that the reinterpretation makes sense.

Arduino
The line you identified in the Arduino file is fairly complicated when it's fully expanded, so we'll break it down a bit. As I think you've identified, it's defining a macro called F(), and that macro takes a parameter called string_literal.

As the name suggests, it's intended to be used with a string literal, F("like this"). Under the surface, the compiler treats a string literal as a pointer to an array of characters; or in other words, char *.

Inside the F() macro, the string literal is put into another macro, called PSTR(). That basically adds a whole bunch of other stuff which tells the compiler to store the string data in program space (where the sketch lives on your Arduino) rather than SRAM (where the variables live).

At this point, the reinterpret_cast comes into play. All the stuff in PSTR() is important, but it doesn't really affect the data type being seen by the cast. You can basically imagine it acting something like this:

char *ptr = "my string data";
reinterpret_cast<const __FlashStringHelper *>(ptr);

__FlashStringHelper is a class, which means its type is unrelated to char *. That's why we need to reinterpret it, so the compiler knows that we're taking responsibility for the safety of the operation. When the result of the cast is used, it will act like a pointer to a __FlashStringHelper object, meaning its methods can be used to access/process the string data.

In reality, no instance of __FlashStringHelper is actually created. The underlying data is still just our string literal. This is one of the interesting aspects of C++ -- you can actually call methods of an object which doesn't exist, as long as the object doesn't try to access non-existent member data.

其他提示

reinterpret_cast operator is used for typecasting.It is to convert one pointer type to another.

Here PSTR is typecasted (converted) to __FlashStringHelper*

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top