Публичный размер для определенной структуры

У меня есть небольшой модуль скрытия данных, который выглядит следующим образом:

/** mydata.h */
struct _mystruct_t;
typedef struct _mystruct_t mystruct;

mystruct *newMystruct();
void freeMystruct( mystruct** p );

/** mydata.c */
#include "mydata.h"
struct _mystruct_t {
    int64_t data1;
    int16_t data2;
    int16_t data3;
};
// ... related definitions ... //

По большей части это то, что я хочу; Несмотря на простоту, структура имеет строгие требования согласованности, и я действительно не хочу предоставлять доступ к членам данных.

Проблема в том, что в клиентском коде я хотел бы включить структуру в другую структуру, которую я хотел бы разместить в стеке. Прямо сейчас я прыгаю через обручи, чтобы освободить mystruct*с в некотором клиентском коде. Так как а) mystruct довольно маленький, и я действительно не думаю, что он скоро станет большим, и б) это не проблема, что клиентский код должен перекомпилироваться, если я когда-нибудь изменю mystruct, я хотел бы сделать размер mystruct общедоступным (т.е. в шапке).

Я рассмотрел две возможности:

/** mydata.h */
typedef struct {
    // SERIOUSLY DON'T ACCESS THESE MEMBERS
    int64_t data1;
    int16_t data2;
    int16_t data3;
} mystruct;

Я думаю, что недостатки здесь говорят сами за себя.

ИЛИ ЖЕ

/** mydata.h */
#define SIZEOF_MYSTRUCT (sizeof(int64_t)+sizeof(int16_t)+sizeof(int16_t))
// everything else same as before...

/** mydata.c */
// same as before...
_Static_assert (SIZEOF_MYSTRUCT == sizeof(mystruct), "SIZEOF_MYSTRUCT is incorrect")

Конечно, это кажется неидеальным, так как мне приходится обновлять это значение вручную, и я не знаю, может ли / как выравнивание структуры на самом деле привести к тому, что это будет некорректно (я думал о статическом утверждении во время написания этого вопроса, оно частично затрагивает это беспокойство).

Является ли один из них предпочтительным? Или, что еще лучше, есть ли какая-нибудь хитрая уловка, чтобы обеспечить фактическое определение структуры в заголовке, а позже как-то скрыть возможность доступа к членам?

3 ответа

Решение

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

/** in mydata.h */
typedef const struct { const char data[12]; } mystruct;
mystruct createMystruct();
int16_t exampleMystructGetter( mystruct *p );
// other func decls operating on mystruct ...


/** in mydata.c */
typedef union {
    mystruct public_block;
    struct mystruct_data_s {
        int64_t d1;
        int16_t d2
        int16_t d3;
    } data;
} mystruct_data;

// Optionally use '==' instead of '<=' to force minimal space usage
_Static_assert (sizeof(struct mystruct_data_s) <= sizeof(mystruct), "mystruct not big enough");

mystruct createMystruct(){
    static mystruct_data mystruct_blank = { .data = { .d1 = 1, .d2 = 2, .d3 = 3 } };
    return mystruct_blank.public_block;
}

int16_t exampleMystructGetter(mystruct *p) {
    mystruct_data *a = (mystruct_data*)p;
    return a->data.d2;
}

В gcc 4.7.3 он компилируется без предупреждений. Простая тестовая программа для создания и доступа через геттер также компилируется и работает как положено.

Я бы остался с настроенным временем генерации #define описывая размер mystruct и, возможно, typedef char[SIZEOF_MYSTRUCT] opaque_mystruct упростить создание заполнителей для mystruct.

Вероятно, идея configure time actions заслуживает некоторых объяснений. Общая идея состоит в том, чтобы

  1. поместите определение mystruct в частный, не экспортируемый, но тем не менее распределенный заголовок,
  2. создайте небольшое тестовое приложение, которое собирается и выполняется перед библиотекой. Тестовое приложение #include закрытый заголовок и напечатает фактический sizeof (mystruct) для данного компилятора и параметров компиляции
  3. создать соответствующий скрипт, который будет создавать библиотеку config.h с #define SIZEOF_MYSTRUCT <calculated_number> и, возможно, определение opaque_mystruct.

Удобно автоматизировать эти шаги, например, с помощью приличной системы сборки.cmake, gnu autotools или любой другой при поддержке configure этап. Фактически все упомянутые системы имеют встроенные средства, которые упрощают всю задачу до вызова нескольких предопределенных макросов.

Вы можете создавать разные .h файл, распространяемый конечному пользователю, который определяет вашу секретную структуру как байтовый массив (вы не можете скрыть данные без криптовалюты / контрольной суммы больше, чем просто говоря "вот некоторые байты"):

typedef struct {
    unsigned char data[12];
} your_struct;

Вы просто должны убедиться, что обе структуры одинаковы для всех компиляторов и опций, таким образом используя __declspec(align()) (для VC) в коде вашей библиотеки, например:

// Client side
__declspec(align(32)) typedef struct {
    int64_t data1;
    int16_t data2;
    int16_t data3;
} mystruct;

Чтобы структура не была длиной 16В вместо обычно ожидаемых 12В. Или просто использовать /Zp опция компилятора.

Другие вопросы по тегам