История изменений
Исправление 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-а тебе вообще был не нужен.