To understand what recursion is, you must first understand recursion
Usually we write recursive functions with the exit condition all by itself:

void writeLine(const char c, const int8_t num)
{
  if (num &gt; 0)
  {
    std::cout &lt;&lt; c;
    writeLine(c, num - 1);
  }
}


I would normally write this as:

void writeLine(const char c, const int8_t num)
{
  if (num &lt;= 0)
  {
      return;
  }

  std::cout &lt;&lt; c;
  writeLine(c, num - 1);
}


No real reason apart from tradition I suppose. But it does show tail recursion more clearly. The compiler can easily optimize tail recursion into a loop for you. But it also makes it obvious to the human eye where tail recursion is.

A static member of a recursive function!!!

void writePyramid(const char c, const int8_t h)
{
  static int count;
  count++;


That's a bit strange. You normally pass all values as parameters between iterations. If you need a defaulting starting value then you can either use a wrapper function that calls your recursive function and provides the initial value. Or have a parameter that defaults to the start value but internally you increment it (I choose the second here).

void writePyramid(const char c, const int8_t h, int count = 0)
{
  if (h &lt;= 0)
  {
    return;
  }

  writeLine(' ', (2 * h - 1) / 2);
  writeLine(c, (2 * count - 1));
  std::endl(std::cout);
  writePyramid(c, h - 1, count + 1); 
}


I know that Static storage duration objects are zero initialized. But how many of your peers know this. It would have been nice to give them a hint.

static int count = 0;


Going back to your wrapper code:

All the switch statements contain the same bit of code:

  std::cout &lt;&lt; "Enter number of rows: ";
  std::cin &gt;&gt; rows;
  std::cout &lt;&lt; "Enter number of columns: ";
  std::cin &gt;&gt; cols;
  std::cout &lt;&lt; "Enter character to build with (*): ";
  type = getSanitizedChar();


Keep it DRY. Either move this into a separate function (called from each case). Or move it to before the switch intirally.