LINUX.ORG.RU

const поля однократной инициализации в классе и оптимизация

 ,


0

2

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

Те вместо

#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>. Возможно компилятор достаточно не дебил и не рожает одинаковые методы для разных размеров а просто сам создает новое динамическое поле размера? Короче как сделать лучше?

И да. Это все чтобы экономить на спичках в условиях мелко контроллеров.

★★★★

Возможно компилятор достаточно не дебил и не рожает одинаковые методы для разных размеров а просто сам создает новое динамическое поле размера?

Думаю, такое поведение было бы очень проблемным, так что вряд ли. А вот если метод от размера не зависит вообще - то можно и объединить.

Короче как сделать лучше?

Зависит от обстоятельств. Если все экземпляры по факту с одинаковым размером то статический выиграет буквально по всем критериям. Если есть с разным, то - динамический размер, пусть и в константе - меньше объём кода. Статический размер - потенциально больше скорость, но не обязательно (могут быть фокусы с не влезанием слишком большого кода в кеш). И наверно больше общая багоустойчивость (нет поля размера в памяти, которое можно побить, зато есть однозначная константа времени компиляции, которую компилятор может где-то проверить).

firkax ★★★★★
()

в условиях мелконтроллеров вряд ли может возникнуть такая надобность в таком разнообразии стеков, да еще с внешней аллокацией.

просто пишешь темплейт stack<elem_type, size> и не мучишь ж. он аллокирует во внутреннем массиве.

вообще стек желательно иметь фиксированного размера и проверять на переполнение (например что осталось N свободных элементов) поскольку очень большие стеки - это скорее порок алгоритмов или ошибки в программах. стеки это рекурсивные вычисления. а они не должны быть глубокими.

void push(Value const& v)

что за куета? небось (const Value& v)

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
#define BGA__ARRAY_SIZE(arrayArg) (sizeof((arrayArg)) / sizeof((arrayArg)[0]))

М-да. А чем std::size не устраивает? Нет поддержки c++17?

Ну тогда хотя бы

template <typename T, std::size_t N>
constexpr std::size_t array_size(T const (&)[N]) {
    return N; 
};
fluorite ★★★★★
()
Ответ на: комментарий от bga_

там у тебя точно циклический буфер? он же в литературе RingBuffer.

такой буфер должен иметь минимальный интерфейс прмерно такой

class RingBuffer
{
  bool put(const Data&); ///положить в буфер
  bool get(Data&); ///взять из буфера
  int count(); ///сколько элементов данных в буфере
  bool is_empty(); ///пуст, get возвратит false
  bool is_full(); ///полон, put возвратит false
}

у тебя get вообще нет похоже и сама реализация странная.

реализация делается либо на двух индексах - текущее начала и конец буфера, или начало и размер, тогда int count() элементарен.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

что за куета? небось (const Value& v)

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

rumgot ★★★★★
()
Ответ на: комментарий от alysnix

Это одноиндексный который сначала забивает буфер первым значением а потом обновляет ячейки последовательно. Те только push без pop.

Тот о каком ты говоришь в репе нету. Я думаю как его лучше написать. Через первый или второй подход. Пока что написано на втором через однократно инициализируемые const.

https://clbin.com/1sEC9

Там разные варианты. Какой меньше флеша сожрет.

bga_ ★★★★
() автор топика
Последнее исправление: bga_ (всего исправлений: 1)
Ответ на: комментарий от rumgot

Ну я так стал писать из-за указателей. const Type* и Type* const это сильно разные вещи. const после типа это явно константа в то время как до - это зависит от контекста. Но сейчас думаю что наверное начал зря. Визуально const должен быть первым.

bga_ ★★★★
() автор топика
Ответ на: комментарий от bga_

Но сейчас думаю что наверное начал зря. Визуально const должен быть первым.

Именно. Читается же код слева-направо.

xaizek ★★★★★
()

Равноценная ли эта подмена?

Что ты под этим понимаешь? Так-то отвалится копирование, хотя можно его делать через placement new + std::launder. А так, константные поля - говнокод. Хотя, если для МК это там действительно существенно (в чём я сильно сомневаюсь) … .

kvpfs ★★
()
Ответ на: комментарий от hatred

Type const * const varName. Речь про такой случай. В общем виде const после типа означает что это это констаный модификор

bga_ ★★★★
() автор топика
Ответ на: комментарий от bga_

Я это понял: const до * относится к данным по указателю, а после - к переменной, что хранит указатель) а до или после типа - тут разницы нет. Потому и написал, что контекста нет, всё очевидно.

Предлагаю замять для ясности)

hatred ★★★
()
Последнее исправление: hatred (всего исправлений: 1)
Ответ на: комментарий от hatred

Лучше бы ответил по теме. Кто нибудь вообще так с константами делает? Или я изобрел НЕХ?

bga_ ★★★★
() автор топика
Ответ на: комментарий от bga_

Я делал. Но без первого const. Структура мапилась на область памяти с регистрами переферийного устройства. Указатель на другой адрес указывать не может по понятным причинам. Там же волатайл, но это детали.

hatred ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.