Хочу использовать вместо постоянного выбора между обычным изменяемым полем и заданием константы из параметров шаблона
Те вместо
#include <cstdint>
#include <cstdlib>
#define BGA__ARRAY_SIZE(arrayArg) (sizeof((arrayArg)) / sizeof((arrayArg)[0]))
template<class ValueArg, size_t sizeArg, class IndexArg = size_t>
struct Stack_Storage_Static {
typedef ValueArg Value;
typedef IndexArg Index;
static const Index size = sizeArg;
Value data[size];
};
template<class ValueArg, class IndexArg = size_t>
struct Stack_Storage_Dynamic {
typedef ValueArg Value;
typedef IndexArg Index;
Value* data;
Index size;
};
template<class StorageArg>
struct Stack {
typedef StorageArg Storage;
typedef typename Storage::Value Value;
typedef typename Storage::Index Index;
Storage storage;
Index topIndex = 0;
Index size() const {
return this->storage.size;
}
void push(Value const& v) {
if(this->topIndex < this->size()) {
this->storage.data[this->topIndex++] = v;
};
}
};
int main() {
Stack<Stack_Storage_Static<int, 16> > staticStack;
staticStack.push(1);
int dynamicStack_data[16];
Stack<Stack_Storage_Dynamic<int> > dynamicStack;
dynamicStack.storage.data = dynamicStack_data;
dynamicStack.storage.size = BGA__ARRAY_SIZE(dynamicStack_data);
dynamicStack.push(1);
return 0;
}
такое
#include <cstdint>
#include <cstdlib>
#define BGA__ARRAY_SIZE(arrayArg) (sizeof((arrayArg)) / sizeof((arrayArg)[0]))
template<class ValueArg, class IndexArg = size_t>
struct Stack {
typedef ValueArg Value;
typedef IndexArg Index;
private:
Index topIndex = 0;
//# спрячем поля чтобы гарантированно пользователь не смог их изменить. Даже через const_cast. Тк мы и сами не можем изменить поля то фактически они есть жеткие константы
Value* const data;
const Index m_size;
public:
Stack(Value* data_, Index size_): data(data_), m_size(size_) {
}
Index size() const {
return this->m_size;
}
void push(Value const& v) {
if(this->topIndex < this->size()) {
this->data[this->topIndex++] = v;
};
}
};
//# тут компилятор знает адрес fixedStack_data и размер и может заинлайнить их в методы. Более того - вообще выкинуть память под указатель на данные и размер если все методы есть инлайн. Те фактически это должно работать как Stack<Stack_Storage_Static<int, 16> > но не инстанцировать новые методы класса на каждый размер данных
int fixedStack_data[16];
Stack<int> fixedStack(fixedStack_data, BGA__ARRAY_SIZE(fixedStack_data));
int main() {
fixedStack.push(1);
size_t dynamicStack_size = 16; //# но может быть задано пользователем
//# тут уже dynamicStack_data с неизвестным адресом и размером. Оптимизация не выйдет. Работает как Stack<Stack_Storage_Dynamic<int> >
int* dynamicStack_data = new int[dynamicStack_size];
Stack<int> dynamicStack(dynamicStack_data, dynamicStack_size);
dynamicStack.push(1);
return 0;
}
Что это дает? Меньший объем исходного кода. Меньше бойлерплейтной возни с шаблонами. Значительно меньший шанс случайно нарожать кучу одинаковых методов, отличающихся только размером контейнера (было такое).
Понятно что нужно проверять работает ли оптимизация на каждом интересующем компиляторе. Но тут вопрос чисто теоретический. Равноценная ли эта подмена?
И сразу параллельный вопрос. Допустим ::std::array<int, size>. Возможно компилятор достаточно не дебил и не рожает одинаковые методы для разных размеров а просто сам создает новое динамическое поле размера? Короче как сделать лучше?
И да. Это все чтобы экономить на спичках в условиях мелко контроллеров.