Why is size required for this structure?

I read the "Start of OpenGL Game Programming Second Edition" and came across this definition of structure:

typedef struct tagPIXELFORMATDESCRIPTOR { WORD nSize; // size of the structure WORD nVersion; // always set to 1 DWORD dwFlags; // flags for pixel buffer properties ... } 

"The first of the most important fields in the structure is nSize. The field should always be set equal to the size of the structure, for example this: pfd.nSize = sizeof (PIXELFORMATDESCRIPTOR); This is a general requirement for data structures that are passed as pointers. Often the structure must know its size and how much memory was allocated for it during various operations. The size of the field allows you to easily and accurately access this information "(p. 24)

Why does the structure require the user to pass size? Can code that uses this structure not just use sizeof () if necessary?

+7
c ++ struct sizeof
source share
6 answers

There are at least two possible reasons for this.

  • The exact definition of the structure will change over time, as a library API is being developed that uses it. New fields will be added at the end, changing the structure definition and changing its sizeof . However, legacy code will still provide an โ€œolderโ€ smaller structure for the same API functions. To ensure that both old and new code works, runtime information is required. Formally, this field is used for the nVersion field. This field alone should be sufficient to tell the API which version of the API the calling code should use and how many fields it allocates in the structure. But only for added security, size information can be provided through the nSize independent field, which is a good idea.

  • The structure contains optional or flexible information (regardless of the version of the API). The fill code will either determine what information you need or do not need based on this size, or crop flexible size information depending on the size you request. This may be especially appropriate if the structure has a flexible array element at the end (in the lines โ€œhit crackedโ€ or such).

In this particular case ( PIXELFORMATDESCRIPTOR struct from the Windows API), this is the first reason that applies, since there is nothing flexible in this structure and associated API.

+6
source share

This allows you to change the structure over time. As new fields are added at the end, the size field tells you which version to use.

+4
source share

Can code that uses this structure not just use sizeof () if necessary?

To make an idea - do not use sizeof to determine the size of the message. This structure is very common in server programming when using socket communication, as well as in WinAPI.

When a binary or fixed-width protocol is first developed, individual messages are defined by specific fields, each of which has a separate size. Client code that reads these messages โ€” either outside the socket or some other buffer used in interprocess communication โ€” must know how much data needs to be read for this message before proceeding to the next message. This is especially true if multiple messages are sent in the same frame or buffer.

Consider if you get a populated data buffer and it has three PIXELFORMATDESCRIPTOR messages. If you know the size of each message, you can correctly switch from one message to another when processing the buffer. How do you know the size of each message?

You can simply use sizeof (PIXELFORMATDESCRIPTOR) if you know that the message size will never change, but there are problems associated with this approach. Firstly, even if the specifications say that the message size will never change, sometimes they do not care when the original developer changes his mind. It happens. Secondly, if your code was designed against one version of the specification, and the server sends messages based on another version of the specification, if the message size changes, your sizeof no longer reflects the true size of the message on the wire, and very bad things will happen. Thirdly, if there is a message in the buffer about which you do not know anything in the code, you have nothing to do sizeof , and you will not be able to process the rest of the buffer.

Checking the sizeof to determine the size of the message on the wire is not a sustainable approach. It is best for the protocol to tell you in real time how large each message is, and process many bytes from the buffer when parsing the message. If the message size is in one place for each type of message (which is recommended when developing such protocols), you can even correctly display messages from a buffer that you know nothing about.

This approach also smooths the update path when the protocol changes. There are several protocols in my work that I program against that do not include the size of the message on the wire. When these messages change, we must make a "hot swap" of one client version to the next one, coordinated with the exact server update time. Imagine the pain that occurs when hundreds of servers are scattered around the world that process this data. If the protocol sent the message size on the wire, we could take a more balanced approach to updating the client software when updating the server - even when installing the new version of the client software in production before or after the server was updated.

+3
source share

The size field can also tell the recipient how much memory is allocated for the structure.

This method is commonly used for messages, especially in embedded systems and when copying a message is necessary.

+1
source share

Imagine you are a developer creating a Windows API. You have a specific set of API calls defined, documented, and released by the OS. Many of your current API calls accept structure pointers as input arguments to skip multiple input values โ€‹โ€‹without a huge number of input arguments.

Now developers are starting to write code for your OS.

A few years later, you decided to create a new version of Windows. You have some requirements:

  • Programs compiled for a previous version of the OS should still run on your new OS - (For this, the API must be backward compatible).
  • You want to expand your API - (added new API calls).
  • You want to allow developers to use their existing code (which they wrote for older windows) and allow them to compile and execute it on the new OS.

OK - for your old programs to work, your new API should have the same routines with the same arguments, etc.

Now how to expand your API? You can add new API calls, but what if at the same time you want to use the old code and use some new bizarre functions without any code changes?

Usually, API procedures require a lot of information, but it is inconvenient to create procedures that have many formal arguments. Therefore, it often happens that one of the formal arguments is the apologer of the structure containing the properties that you want to convey to your routine. This simplifies the API. For example:

your old code:

 struct abc { int magicMember; // ;-) int a; int b; int c; }; void someApiCall( struct abc *p, int blaBla ); 

Now, if you decide to expand your "someApiCall" by providing more information without changing the signature, you simply change your structure.

your new code:

 // on new OS - defined in a header with the same name as older OS // hence no includes changes struct abc { int magicMember; // ;-) int a; int b; int c; int new_stuff_a; int new_stuff_b; }; void someApiCall( struct abc *p, int blaBla ); 

You saved the standard signature and at the same time allowed both the old and the new code to work. The only secret is magicMember , which can be considered as the revision number of the structure or - if in new versions you just add new members - the size of the structure. In both cases, your "someApiCall" will be able to distinguish between two types of the "same" structure, and you will be able to make an API call from the old and new code.

If one is picky - (s), he can say that these are not the same structures. Really not. They have the same name to prevent code changes.

For a real example, check the RegisterClassEx API call and the WNDCLASSEX structure is required

+1
source share

In most cases, if you access tagPIXELFORMATDESCRIPTOR with pointers like tagPIXELFORMATDESCRIPTOR* , you probably don't need a member to indicate the size; sizeof will always give you the correct size:

 void func(tagPIXELFORMATDESCRIPTOR *ptr) { // sizeof *ptr is the correct size } 

But if you play tricks using pointers of different types, possibly using pointer casting, then the size member at the beginning of the structure can allow you to determine the size of the structure without knowing its type.

For example, you can define a type that contains only a size element:

 struct empty { WORD nSize; }; 

Then, while you carefully set the nSize member to the correct value for each object you create (and while nSize always in the same place in each structure), you can get the size of the structure without knowing its actual type:

 void func(empty *ptr) { // sizeof *ptr is incorrect // ptr->nSize is the actual size (if you've done everything right) // That ok if we don't need any information other than the size } ... tagPIXELFORMATDESCRIPTOR obj; ... func(reinterpret_cast<empty*>(ptr)); 

What can not be said that this is a good idea.

If you can just use the appropriate type of pointer without doing a pointer cast, you have to do it.

If you cannot, C ++ provides much cleaner and more reliable ways (especially inheritance) for defining related types. It is much better to define what I called empty (or perhaps it should be called as descriptor ) as a class, and then define tagPIXELFORMATDESCRIPTOR as a subclass.

I am not familiar with OpenGL, but I suspect that it was originally designed to use C-style pseudo-inheritance. You may need to stick with this model if you need to work with OpenGL objects in C or C ++ code.

+1
source share

All Articles