Use #pragma pack with #define on Borland C++
Asked Answered
P

2

5

I am trying to pack some structs with Borland C++Builder (XE6) (in the future: bcc).

I am using a library which uses the following construct to create structs:

#ifdef _MSC_VER
    #define PACKED_BEGIN __pragma(pack(push, 1))
    #define PACKED 
    #define PACKED_END __pragma(pack(pop))
#elif defined(__GNUC__)
    #define PACKED_BEGIN
    #define PACKED  __attribute__((__packed__))
    #define PACKED_END
#endif


PACKED_BEGIN
struct PACKED {
    short someSampleShort;
    char sampleByte;
    int sampleInteger;
} structType_t;
PACKED_END

The bcc compiler does not like the MSC __pragma, and does not like preprocessor directives inside of macros although it is described on their website:

#define GETSTD #include <stdio.h>

My Question is: Is there any possibility to use this construct with the Borland Compiler for packing a struct without using:

#pragma pack(1) 

to pack every struct?

Are there any workarounds for this?

Palacio answered 14/4, 2015 at 14:28 Comment(4)
You may still use #include "packed_begin.h" and #include "packed_end.h" (without guards)Simeon
Thanks for your Comment, but i cant use #include in a macro because borland does not support this. I could rewrite the Library, but i was hoping for a possibility with macrosPalacio
@Palacio And well it should not, it's not legal C++. I believe Jarod meant you could replace the macro lines with that.Hoyle
Yeah, i just thought there would be a possibility for not editing the library files. I just could do preg_replace, but when the library gets updated, i have to do this again. With macros, i just can use the new files and it will still be compilable.Palacio
C
3

As you stated, C++Builder does not support preprocessor statements inside of macros. This is documented on Embarcadero's site:

#define (C++)

After each individual macro expansion, a further scan is made of the newly expanded text. This allows for the possibility of nested macros: The expanded text can contain macro identifiers that are subject to replacement. However, if the macro expands into what looks like a preprocessing directive, the directive will not be recognized by the preprocessor.

The reason for that is because the # character inside of a macro is reserved for the preprocessor's stringizing operator.

Some compilers, including MSVC, get around that restriction with the __pragma() compiler extension, or the C99/C++x0 _Pragma() extension. C++Builder's Windows 32bit compiler does not support either of those. However, its Windows 64bit and mobile compilers (which are all based on clang and support C++11) DO support both of them. So you can add support for those compilers in the macros like this:

#if defined(__BORLANDC__)
    #if defined(__clang__)
        #define PACKED_BEGIN __pragma(pack(push, 1))
        #define PACKED 
        #define PACKED_END __pragma(pack(pop))
    #else
        #error Cannot define PACKED macros for this compiler
    #endif
#elif defined(_MSC_VER)
    #define PACKED_BEGIN __pragma(pack(push, 1))
    #define PACKED 
    #define PACKED_END __pragma(pack(pop))
#elif defined(__GNUC__)
    #define PACKED_BEGIN
    #define PACKED  __attribute__((__packed__))
    #define PACKED_END
#else
    #error PACKED macros are not defined for this compiler
#endif

If you want to support the C++Builder Windows 32bit compiler, you will have to move the logic into .h files that use #pragma for it, and then you can #include those files where needed (at least until the compiler is updated to support clang/C++11 - which Embarcadero is currently working on):

pack1_begin.h:

#if defined(__BORLANDC__)
    #define PACKED_BEGIN
    #define PACKED 
    #define PACKED_END
    #pragma pack(push, 1)
#elif defined(_MSC_VER)
    #define PACKED_BEGIN __pragma(pack(push, 1))
    #define PACKED 
    #define PACKED_END __pragma(pack(pop))
#elif defined(__GNUC__)
    #define PACKED_BEGIN
    #define PACKED  __attribute__((__packed__))
    #define PACKED_END
#else
    #error PACKED macros are not defined for this compiler
#endif

pack_end.h:

#if defined(__BORLANDC__)
    #pragma pack(pop)
#endif

Then you can do this:

#include "pack1_begin.h"
PACKED_BEGIN
struct PACKED {
    short someSampleShort;
    char sampleByte;
    int sampleInteger;
} structType_t;
PACKED_END
#include "pack_end.h"

If you take this approach, you can just drop PACKED_BEGIN/PACKED_END altogether:

pack1_begin.h:

#if defined(__BORLANDC__) || defined(_MSC_VER)
    #define PACKED
    #pragma pack(push, 1)
#elif defined(__GNUC__)
    #define PACKED  __attribute__((__packed__))
#else
    #error PACKED macro is not defined for this compiler
#endif

pack_end.h:

#if defined(__BORLANDC__) || defined(_MSC_VER)
    #pragma pack(pop)
#endif

#include "pack1_begin.h"
struct PACKED {
    short someSampleShort;
    char sampleByte;
    int sampleInteger;
} structType_t;
#include "pack_end.h"
Cuttlebone answered 14/4, 2015 at 20:0 Comment(1)
I'd just add that a conforming C++ compiler cannot support directives in macro replacement. C++11 16.34./3: "The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one, ..."Hoyle
H
0

The standard offers one extra alternative for writing pragmas: the _Pragma operator:

#define PACKED_BEGIN _Pragma("pack(push, 1)")
#define PACKED 
#define PACKED_END _Pragma("pack(pop)")

If the Borland compiler supports it, it should work.

Hoyle answered 14/4, 2015 at 15:9 Comment(2)
Thank you for your Answer, but unfortunately the Borland compiler (my version) does not support _Pragma.Palacio
@user2358582: C++Builder's 32bit compiler does not, but its 64bit and mobile compilers do.Cuttlebone

© 2022 - 2024 — McMap. All rights reserved.