LINUX.ORG.RU

История изменений

Исправление kawaii_neko, (текущая версия) :

какие тут подводные камни, что можно улучшить?

1. Выравнивание может внезапно выстрелить на sse-инструкциях.

2. Работа с uint16_t в 64-битном режиме тормознутая

если индекс вышел за пределы массива генерируется ошибка.

Для этого необязательно хранить размеры индивидуальных массивов достаточно сделать +1 элемент в offsets и при доступе проверять, что (T*)&data[off[n]] + index < (T*)&data[off[n+1]]

Ну раз «возможно двумерные», тогда да, придется где-то хранить одну размерность.

как к этому малой кровью прикрутить интроспекцию

Вместо бесполезного sz сделать массив с кодами типов. Для этого нужно перейти от объявления полей к заданию списка полей. Кажется, что сделать это можно как макросами, так и шаблонами. Самый простой и очевидный способ

#define OFF(x) ((x) & 0x00FFFFFFu)
#define ROW_LEN(x) ((x) >> 24)
#define BASE_PTR(type, n) ((type*)(&data[offsets[n]])
#define COUNT(type, name) +1
#define NAME2ID(type, name) f_ ##name,
#define ACCESSOR(type, name) \
    // one-dimensional accessor \
    type& name(size_t index) { \
        constexpr size_t n = f_##name; \
        if (ROW_LEN(offsets[n]) != 0) throw runtime_error(#name " is a two-dimensional array!"); \
        if (BASE_PTR(type, n) + index >= BASE_PTR(type, n + 1)) throw runtime_error(#name "'s index out of range"); \
        return BASE_PTR(n)[index]; \
        } \
    // two-dimensional accessor \
    type& name(size_t row, size_t col) { size_t index = f_##name; ... }

struct array_info {
  const char *name;
  const char *type;
  size_t nrows;
  size_t ncols;
};

struct E {
#define FIELDS(XX) XX(double, a) XX(float, b) XX(int, c)
enum { FIELDS(NAME2ID) }; // f_a = 0, f_b = 1, f_c = 2
static constexpr size_t N = 0 FIELDS(COUNT);
uint32_t offsets[N + 1]; // оффсеты полей, пусть в старшем байте хранится длина строки для многомерного массива
FIELDS(ACCESSORS); // сгенерировались функции для доступа
void introspect(vector<array_info> &info) {
  info.resize(N + 1);
  size_t i = 0;
#define XX(type, name) \
  info[i].name = #name; \
  info[i].type = #type; \
  info[i].ncols = ROW_LEN(offsets[i]); \
  info[i].nrows = (OFF(offsets[i + 1]) - OFF(offsets[i])) / sizeof(type) / ROW_LEN(offsets[i]); \
  i++;
  FIELDS(XX);
#undef XX
}
#undef FIELDS
};

И по итогу оказывается, что номер field-а тебе вообще был не нужен.

Исправление kawaii_neko, :

какие тут подводные камни, что можно улучшить?

1. Выравнивание может внезапно выстрелить на sse-инструкциях.

2. Работа с uint16_t в 64-битном режиме тормознутая

если индекс вышел за пределы массива генерируется ошибка.

Для этого необязательно хранить размеры индивидуальных массивов достаточно сделать +1 элемент в offsets и при доступе проверять, что (T*)&data[off[n]] + index < (T*)&data[off[n+1]]

Ну раз «возможно двумерные», тогда да, придется где-то хранить одну размерность.

как к этому малой кровью прикрутить интроспекцию

Вместо бесполезного sz сделать массив с кодами типов. Для этого нужно перейти от объявления полей к заданию списка полей. Кажется, что сделать это можно как макросами, так и шаблонами. Самый простой и очевидный способ

#define OFF(x) ((x) & 0x00FFFFFFu)
#define ROW_LEN(x) ((x) >> 24)
#define BASE_PTR(type, n) ((type*)(&data[offsets[n]])
#define COUNT(type, name) +1
#define NAME2ID(type, name) f_ ##name,
#define ACCESSOR(type, name) \
    // one-dimensional accessor \
    type& name(size_t index) { \
        constexpr size_t n = f_##name; \
        if (ROW_LEN(offsets[n]) != 0) throw runtime_error(#name " is a two-dimensional array!"); \
        if (BASE_PTR(type, n) + index >= BASE_PTR(type, n + 1)) throw runtime_error(#name "'s index out of range"); \
        return BASE_PTR(n)[index]; \
        } \
    // two-dimensional accessor \
    type& name(size_t row, size_t col) { size_t index = f_##name; ... }

struct array_info {
  const char *name;
  const char *type;
  size_t nrows;
  size_t ncols;
};

struct E {
#define FIELDS(XX) XX(double, a) XX(float, b) XX(int, c)
enum { FIELDS(NAME2ID) }; // f_a = 0, f_b = 1, f_c = 2
static constexpr size_t N = 0 FIELDS(COUNT);
uint32_t offsets[N + 1]; // оффсеты полей, пусть в старшем байте хранится длина строки для многомерного массива
FIELDS(ACCESSORS); // сгенерировались функции для доступа
void introspect(vector<array_info> &info) {
  info.resize(N + 1);
  size_t i = 0;
#define XX(type, name) \
  info[i].name = #name; \
  info[i].type = #type; \
  info[i].ncols = ROW_LEN(offsets[i]); \
  info[i].nrows = (OFF(offsets[i + 1]) - OFF(offsets[i])) / sizeof(type) / ROW_LEN(offsets[i]); \
  i++;
  FIELDS(XX);
#undef FIELDS
}
#undef FIELDS

И по итогу оказывается, что номер field-а тебе вообще был не нужен.

Исходная версия kawaii_neko, :

какие тут подводные камни, что можно улучшить?

1. Выравнивание может внезапно выстрелить на sse-инструкциях.

2. Работа с uint16_t в 64-битном режиме тормознутая

если индекс вышел за пределы массива генерируется ошибка.

Для этого необязательно хранить размеры индивидуальных массивов достаточно сделать +1 элемент в offsets и при доступе проверять, что (T*)&data[off[n]] + index < (T*)&data[off[n+1]]

Ну раз «возможно двумерные», тогда да, придется где-то хранить одну размерность.

как к этому малой кровью прикрутить интроспекцию

Вместо бесполезного sz сделать массив с кодами типов. Для этого нужно перейти от объявления полей к заданию списка полей. Кажется, что сделать это можно как макросами, так и шаблонами. Самый простой и очевидный способ

#define OFF(x) ((x) & 0x00FFFFFFu)
#define ROW_LEN(x) ((x) >> 24)
#define BASE_PTR(type, n) ((type*)(&data[offsets[n]])
#define COUNT(type, name) +1
#define NAME2ID(type, name) f_ ##name,
#define ACCESSOR(type, name) \
    // one-dimensional accessor \
    type& name(size_t index) { \
        constexpr size_t n = f_##name; \
        if (ROW_LEN(offsets[n]) != 0) throw runtime_error(#name " is a two-dimensional array!"); \
        if (BASE_PTR(type, n) + index >= BASE_PTR(type, n + 1)) throw runtime_error(#name "'s index out of range"); \
        return BASE_PTR(n)[index]; \
        } \
    // two-dimensional accessor \
    type& name(size_t row, size_t col) { size_t index = f_##name; ... }

struct array_info {
  const char *name;
  const char *type;
  size_t nrows;
  size_t ncols;
};

struct E {
#define FIELDS(XX) XX(double, a) XX(float, b) XX(int, c)
enum { FIELDS(NAME2ID) }; // f_a = 0, f_b = 1, f_c = 2
static constexpr size_t N = 0 FIELDS(COUNT);
uint32_t offsets[N + 1]; // оффсеты полей, пусть в старшем байте хранится длина строки для многомерного массива
FIELDS(ACCESSORS); // сгенерировались функции для доступа
void introspect(vector<array_info> &info) {
  info.resize(N + 1);
  size_t i = 0;
  size_t len;
#define XX(type, name) \
  info[i].name = #name; \
  info[i].type = #type; \
  len = ; \
  info[i].ncols = ROW_LEN(offsets[i]); \
  info[i].nrows = (OFF(offsets[i + 1]) - OFF(offsets[i])) / ROW_LEN(offsets[i]); \
  i++;
  FIELDS(XX);
#undef FIELDS
}
#undef FIELDS

И по итогу оказывается, что номер field-а тебе вообще был не нужен.