Используйте пакет #pragma с #define на Borland C++

Я пытаюсь упаковать некоторые структуры с помощью Borland C++Builder (XE6) (в будущем: bcc).

Я использую библиотеку, которая использует следующую конструкцию для создания структур:

#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

Компилятору bcc не нравится MSC __pragma и не нравятся директивы препроцессора внутри макросов, хотя это описано на их веб-сайт:

#define GETSTD #include <stdio.h>

Мой вопрос: есть ли возможность использовать эту конструкцию с компилятором Borland для упаковки структуры без использования:

#pragma pack(1) 

упаковать каждую структуру?

Есть ли обходные пути для этого?


person user2358582    schedule 14.04.2015    source источник
comment
Вы по-прежнему можете использовать #include "packed_begin.h" и #include "packed_end.h" (без охраны)   -  person Jarod42    schedule 14.04.2015
comment
Спасибо за ваш комментарий, но я не могу использовать #include в макросе, потому что borland не поддерживает это. Я мог бы переписать библиотеку, но я надеялся на возможность с макросами   -  person user2358582    schedule 14.04.2015
comment
@user2358582 user2358582 И не должно, это нелегально C++. Я полагаю, Джарод имел в виду, что вы можете заменить строки макроса на это.   -  person Angew is no longer proud of SO    schedule 14.04.2015
comment
Да, я просто подумал, что будет возможность не редактировать файлы библиотеки. Я просто мог бы сделать preg_replace, но когда библиотека обновится, мне придется сделать это снова. С макросами я просто могу использовать новые файлы, и они все равно будут компилироваться.   -  person user2358582    schedule 14.04.2015


Ответы (2)


Как вы сказали, C++Builder не поддерживает операторы препроцессора внутри макросов. Это задокументировано на сайте Embarcadero:

#define (C++)

После каждого отдельного расширения макроса выполняется дальнейшее сканирование вновь расширенного текста. Это допускает возможность вложенных макросов: расширенный текст может содержать идентификаторы макросов, подлежащие замене. Однако, если макрос превращается в нечто, похожее на директиву препроцессора, эта директива не будет распознана препроцессором.

Причина этого в том, что символ # внутри макроса зарезервирован для оператора преобразования строк препроцессора.

Некоторые компиляторы, в том числе MSVC, обходят это ограничение с помощью расширения компилятора __pragma() или расширения C99/C++x0 _Pragma(). Компилятор Windows 32bit C++Builder не поддерживает ни один из них. Однако его компиляторы Windows 64bit и мобильные (все они основаны на clang и поддерживают C++11) DO поддерживают оба из них. Таким образом, вы можете добавить поддержку этих компиляторов в макросы следующим образом:

#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

Если вы хотите поддерживать компилятор Windows 32bit C++Builder, вам придется переместить логику в файлы .h, которые используют для нее #pragma, а затем вы можете #include те файлы, где это необходимо (по крайней мере пока компилятор не будет обновлен для поддержки clang/C++11, над которым сейчас работает Embarcadero):

пакет1_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

пакет_конец.ч:

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

Тогда вы можете сделать это:

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

Если вы выберете этот подход, вы можете просто вообще отказаться от PACKED_BEGIN/PACKED_END:

пакет1_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

пакет_конец.ч:

#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"
person Remy Lebeau    schedule 14.04.2015
comment
Я бы только добавил, что соответствующий компилятор C++ не может поддерживать директивы при замене макросов. C++11 16.34./3: Результирующая последовательность токенов предварительной обработки, полностью замененная макросом, не обрабатывается как директива предварительной обработки, даже если она похожа на таковую, ... - person Angew is no longer proud of SO; 15.04.2015

Стандарт предлагает одну дополнительную альтернативу для написания прагм: оператор _Pragma:

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

Если компилятор Borland его поддерживает, он должен работать.

person Angew is no longer proud of SO    schedule 14.04.2015
comment
Спасибо за ваш ответ, но, к сожалению, компилятор Borland (моя версия) не поддерживает _Pragma. - person user2358582; 14.04.2015
comment
@user2358582 user2358582: 32-битный компилятор C++Builder этого не делает, но его 64-битные и мобильные компиляторы делают это. - person Remy Lebeau; 14.04.2015