Predefined values for some values in a template function like with a #define macro (e.g. template) I want to overload an operator (e.g. operator&lt;&lt;) with different types, but also change only 1 or 2 values in the function itself.

I want to get some fancy output of a vector with the primitive integer types.
I have tried to create a template function like template&lt;int width, typename T&gt; operator&lt;&lt;... but this would need to set the width and typename explicitly with each call of the operator&lt;&lt; e.g.

I have a following minimal code example (which is not the shortest tbh):

main.cpp

#include &lt;iostream&gt;
#include &lt;vector&gt;

#include "utils.h"

using namespace std;

int main(int argc, char* argv[]) {
  vector&lt;int8_t&gt; vec_s_1 = {1, 2, 3, -1, -2, -123};
  vector&lt;uint8_t&gt; vec_u_1 = {1, 2, 3, 127, 128, 129};
  vector&lt;int16_t&gt; vec_s_2 = {1, 2, 3, -1, -2, -123};
  vector&lt;uint16_t&gt; vec_u_2 = {1, 2, 3, 127, 128, 129};
  vector&lt;int32_t&gt; vec_s_3 = {1, 2, 3, -1, -2, -123};
  vector&lt;uint32_t&gt; vec_u_3 = {1, 2, 3, 127, 128, 129};
  vector&lt;int64_t&gt; vec_s_4 = {1, 2, 3, -1, -2, -123};
  vector&lt;uint64_t&gt; vec_u_4 = {1, 2, 3, 127, 128, 129};

  cout &lt;&lt; "vec_s_1: " &lt;&lt; vec_s_1 &lt;&lt; endl;
  cout &lt;&lt; "vec_u_1: " &lt;&lt; vec_u_1 &lt;&lt; endl;
  cout &lt;&lt; endl;
  cout &lt;&lt; "vec_s_2: " &lt;&lt; vec_s_2 &lt;&lt; endl;
  cout &lt;&lt; "vec_u_2: " &lt;&lt; vec_u_2 &lt;&lt; endl;
  cout &lt;&lt; endl;
  cout &lt;&lt; "vec_u_3: " &lt;&lt; vec_u_3 &lt;&lt; endl;
  cout &lt;&lt; "vec_s_3: " &lt;&lt; vec_s_3 &lt;&lt; endl;
  cout &lt;&lt; endl;
  cout &lt;&lt; "vec_s_4: " &lt;&lt; vec_s_4 &lt;&lt; endl;
  cout &lt;&lt; "vec_u_4: " &lt;&lt; vec_u_4 &lt;&lt; endl;

  return 0;
}


utils.h

#ifndef MODULO_SEQUENCE_UTILS_H
#define MODULO_SEQUENCE_UTILS_H

#include &lt;vector&gt;
#include &lt;iomanip&gt;
#include &lt;ostream&gt;
#include &lt;sstream&gt;

using namespace std;

template&lt;typename T&gt;
ostream&amp; operator&lt;&lt;(ostream&amp; os, const vector&lt;T&gt;&amp; obj);

#endif // MODULO_SEQUENCE_UTILS_H


utils.cpp

#include "utils.h"

#define GET_FUNCTION(WIDTH, TYPENAME, MASK) \
template&lt;&gt; ostream&amp; operator&lt;&lt;(ostream&amp; os, const vector&lt;TYPENAME&gt;&amp; obj) { \
  size_t size = obj.size(); \
  os &lt;&lt; "["; \
  for (size_t i = 0; i &lt; size; ++i) { \
    if (i &gt; 0) { \
      os &lt;&lt; ", "; \
    } \
    stringstream ss; \
    ss &lt;&lt; "0x" &lt;&lt; hex &lt;&lt; uppercase &lt;&lt; setw(WIDTH) &lt;&lt; setfill('0') &lt;&lt; (obj[i] &amp; MASK); \
    os &lt;&lt; ss.str(); \
  } \
  os &lt;&lt; "]"; \
  return os; \
}

GET_FUNCTION(2, int8_t, 0xFF)
GET_FUNCTION(2, uint8_t, 0xFF)
GET_FUNCTION(4, int16_t, 0xFFFF)
GET_FUNCTION(4, uint16_t, 0xFFFF)
GET_FUNCTION(8, int32_t, 0xFFFFFFFF)
GET_FUNCTION(8, uint32_t, 0xFFFFFFFF)
GET_FUNCTION(16, int64_t, 0xFFFFFFFFFFFFFFFF)
GET_FUNCTION(16, uint64_t, 0xFFFFFFFFFFFFFFFF)


So far it is working like expected and the output is like I wanted to have it. It is solved by calling #define for each function separately (which is a really looking ugly code).

The output looks like this:

vec_s_1: [0x01, 0x02, 0x03, 0xFF, 0xFE, 0x85]
vec_u_1: [0x01, 0x02, 0x03, 0x7F, 0x80, 0x81]

vec_s_2: [0x0001, 0x0002, 0x0003, 0xFFFF, 0xFFFE, 0xFF85]
vec_u_2: [0x0001, 0x0002, 0x0003, 0x007F, 0x0080, 0x0081]

vec_u_3: [0x00000001, 0x00000002, 0x00000003, 0x0000007F, 0x00000080, 0x00000081]
vec_s_3: [0x00000001, 0x00000002, 0x00000003, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFF85]

vec_s_4: [0x0000000000000001, 0x0000000000000002, 0x0000000000000003, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFF85]
vec_u_4: [0x0000000000000001, 0x0000000000000002, 0x0000000000000003, 0x000000000000007F, 0x0000000000000080, 0x0000000000000081]


I need the different widths of the leading zeros in the output plus the mask for getting the exact hex number for each type.

My question is: How can/could I achieve the same result with only using templates?
The trick is to use a byte_repeater like here. 

template&lt;typename T, std::size_t count, int byte&gt;
struct byte_repeater;

template&lt;typename T, int byte&gt;
struct byte_repeater&lt;T, 1, byte&gt; {
    static constexpr T value = byte;
};

template&lt;typename T, std::size_t count, int byte&gt;
struct byte_repeater {
    static constexpr auto value = (byte_repeater&lt;T, count-1, byte&gt;::value &lt;&lt; CHAR_BIT) | byte;
};


Add a convenient function make_mask

template&lt;typename T, int mask&gt;
struct make_mask {
    static constexpr auto value = byte_repeater&lt;typename std::make_unsigned&lt;T&gt;::type, sizeof(T), mask&gt;::value;
};


And then the ostream&lt;&lt; operator becomes:

template&lt;typename T&gt;
std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const std::vector&lt;T&gt;&amp; obj)
{
    static constexpr auto width = sizeof(T) * 2;
    static constexpr auto mask = make_mask&lt;T,0xFF&gt;::value;
    os &lt;&lt; "[";
    std::for_each(obj.begin(), obj.end() - 1, [&amp;os](const auto&amp; elem) {
        os &lt;&lt; "0x" &lt;&lt; std::hex &lt;&lt; std::uppercase &lt;&lt; std::setw(width) &lt;&lt; std::setfill('0') &lt;&lt; (elem &amp; mask) &lt;&lt; ", ";
    });
    os &lt;&lt; "0x" &lt;&lt; std::hex &lt;&lt; std::uppercase &lt;&lt; std::setw(width) &lt;&lt; std::setfill('0') &lt;&lt; (obj.back() &amp; mask);
    os &lt;&lt; "]";
    return os;
}


LIVE DEMO