#include <cstdint>
#include <iostream>
#include <algorithm>
#include <type_traits>

using namespace std;

constexpr uint16_t MM_len = 128;
uint8_t MM_data[MM_len] = {};
uint16_t MM_index = 0;

// add() variant to copy a buffer into MM_data. Returns updated MM_index or 0
uint16_t add(uint16_t count, uint8_t *arrayOfBytes)
{
	if (!MM_data || count > MM_len - MM_index)
		return 0;
	std::copy_n(arrayOfBytes, count, &MM_data[MM_index]);
	MM_index += count;
	return MM_index;
}

// Terminate args recursion
uint16_t add() { return 0; }

// add() - add a single data element MSB first to MM_data. Returns updated MM_index or 0
template <class T> uint16_t add(T v) {
    uint16_t sz = sizeof(T);    // Size of value to be added

    // Will it fit?
    if (MM_data && sz <= (MM_len - MM_index)) {
      // Yes. Copy it MSB first
      while (sz) {
        sz--;
        MM_data[MM_index++] = (v >> (sz << 3)) & 0xFF;
      }
      // Return updated MM_index (logical length of message so far)
      return MM_index;
    }
    // No, will not fit - return 0
    return 0;
}

// First arg is uint8_t
template <class... Args> 
typename std::enable_if<(sizeof...(Args) > 0), uint16_t>::type
add(uint8_t v, Args... args) {
    return add(v) + add(args...);
}

// First arg is uint16_t
template <class... Args> 
typename std::enable_if<(sizeof...(Args) > 0), uint16_t>::type
add(uint16_t v, Args... args) {
    return add(v) + add(args...);
}

// First arg is uint32_t
template <class... Args>
typename std::enable_if<(sizeof...(Args) > 0), uint16_t>::type
add(uint32_t v, Args... args) {
    return add(v) + add(args...);
}

auto main() -> int
{
    uint8_t a[] = { 4, 5, 6 };
    add(uint16_t{ 1 }, uint8_t{ 2 }, uint32_t{ 3 });
    add(3, &a[0]);
    for (uint16_t i = 0; i < MM_index; ++i)
    	cout << int{ MM_data[i] } << " ";
    cout << endl;
}
