Шаблонные развёртки

Шаблонный развёртки позволяют создавать повторяющийся код на стадии компиляции.

Файл repeat.hpp

Честно признаюсь, что шаблоны сложны для моего понимания.

Код, который можно реализовать с помощью цикла с чётко известной зависимостью от параметра цикла, представляет хорошее повод для «раскрутки». Встроенные средства раскрутки циклов, по моему опыту, ограничены восьмикратной раскруткой, то есть не более восьми вызовов кода цикла, а потом переход в начало.

Шаблонный код, указанный в начале, делает абсолютную раскрутку цикла, без ограничений.

Начать рассмотрение следует снизу вверх.

template<size_t N, typename FunctionType>
fdown_t<N,FunctionType,N> fdown(FunctionType function)
{
  return fdown_t<N,FunctionType,N>(function);
}

Первый параметр шаблона N — число повторений кода. Это всегда целочисленное положительное значение. FunctionType — тип функции. function — сама функция.

Шаблон преобразуется в другой шаблон:

template<size_t N, typename FunctionType, size_t I>
class fdown_t
{
public:
  fdown_t(FunctionType function) : function_(function) {}
  FunctionType operator()()
  {
    function_(I-1);
    return fdown_t<N,FunctionType,I-1>(function_)();
  }
private:
  FunctionType function_;
};

N и FunctionType сохраняют своё значение. А I используется как параметр цикла.
Можно заметить, что собственно функция в теле конструктора вызывается с уменьшенным I на единицу. Именно поэтому шаблон носит имя fdown_t (for down to).

После выполнения функции, выполняется возврат из шаблонной функции с уменьшенным параметром I. После этого цикл повторяется, за одним исключением:

template<size_t N, typename FunctionType>
class fdown_t<N,FunctionType,0>
{
public:
  fdown_t(FunctionType function) : function_(function) {}
  FunctionType operator()() { return function_; }
private:
  FunctionType function_;
};

Можно увидеть, когда параметр I ровен нулю, то используется другой шаблон. Этот шаблон просто возвращает функцию. Таким образом, все выше созданные шаблоны исполняют часть кода перед вызовом return, который ничего не делает. Адрес функции просто передаётся вверх. Возвращаемое значение, равное самой функции может быть проигнорировано, как и сам параметр цикла. Аналогично реализуются fup_t, frangeup_t, frangedown_t, где возрастает параметр от нуля, возрастает от определённого значения, или уменьшается от определённого значения.

Использование kputc.cpp

void drawGlyph(
	u32 uX, //Точка начала отображения (X, Y)
	u32 uY, //
	HANDLE hGlyph, //Ссылка на данные отображения
  eCOLOR foreColor, //Цвета: текста
  eCOLOR backColor   //фон
){
	pu8 hcGlyph=reinterpret_cast<pu8>(hGlyph);
	uY*=fb_width;
	
	fup<16>([&](size_t){
		u8 line=*hcGlyph++;
		pu32 fb_offset=&fb_base[uY];
		fdown<8>([&](size_t bit){
			fb_offset[ uX++ ]=bitif(line,bit,foreColor,backColor);
		})();
		uX+=fb_width-8;
	})();
	
}