Perhaps the simplest implementation prior to C++17 would be:
std::strcpy(buffer, std::to_string(value).c_str());
This does require a temporary buffer (a temporary std::string
) but I would be hesitant to prematurely optimize. In my opinion, this would be the most elegant way to do the conversion -- it's simple and easily understandable.
(Note that it's not possible with your function signature to ensure that the buffer
pointer points to an allocation large enough to hold the stringified value.)
In C++17, you can just use std::to_chars()
(you will have to add the null terminating character yourself with this function; the documentation does not state that it adds one for you).
Perhaps there is middle ground where you can declare a trait to obtain the printf-style format specifier for each numeric type?
#include <cstdio>
template <typename T>
struct printf_specifier
{
static char const * const value;
};
template<> char const * const printf_specifier<char>::value = "%hhd";
template<> char const * const printf_specifier<unsigned char>::value = "%hhu";
template<> char const * const printf_specifier<short>::value = "%hd";
template<> char const * const printf_specifier<unsigned short>::value = "%hu";
template<> char const * const printf_specifier<int>::value = "%d";
template<> char const * const printf_specifier<unsigned int>::value = "%u";
template<> char const * const printf_specifier<long>::value = "%ld";
template<> char const * const printf_specifier<unsigned long>::value = "%lu";
template<> char const * const printf_specifier<long long>::value = "%lld";
template<> char const * const printf_specifier<unsigned long long>::value = "%llu";
template<> char const * const printf_specifier<float>::value = "%f";
template<> char const * const printf_specifier<double>::value = "%f";
template <typename T>
void foo(char *buffer, T value)
{
std::sprintf(buffer, printf_specifier<T>::value, value);
}
I would, however, suggest using snprintf
since it won't overrun your buffer if you give it the number of characters it is allowed to write:
template <typename T>
int foo(char *buffer, std::size_t size, T value)
{
return std::snprintf(buffer, size, printf_specifier<T>::value, value);
}
If even this is too much bloat, you can just do the conversion entirely yourself:
#include <algorithm>
#include <cstdlib>
template <typename T>
void foo(char *buffer, T value)
{
static_assert(std::is_integral<T>::value, "Type of value must be an integral type");
if (value < 0) {
*(buffer++) = '-';
}
char *start = buffer;
while (value != 0) {
*(buffer++) = '0' + std::abs(value % 10);
value /= 10;
}
if (buffer == start) {
*(buffer++) = '0';
} else {
std::reverse(start, buffer);
}
*buffer = '\0';
}
It could be faster to use log10 to figure out how long the string will be and write it from back to front instead of writing it backwards and then reversing it, but I'll leave that option as an exercise for you if you deem it necessary.