C++ Convert Integer to String at Compile Time

C++ convert integer to string at compile time

This is now possible in C++17 and beyond without any external dependencies, with the conversion done entirely at compile-time. I've packaged the code in a short header file, check out constexpr-to-string on GitHub. The code supports different bases and character widths as well.

Examples of use:

const char *number = to_string<2147483648999954564, 16>; // produces "1DCD65003B9A1884"
puts(number);
puts(to_string<-42>); // produces "-42"
puts(to_string<30, 2>); // produces "11110"

// Below requires C++20
puts(f_to_string<3.1415926>); // Defaults to 5-point precision: "3.14159"
puts(f_to_string<{3.1415926, 7}>); // Specify precision: "3.1415926"

Convert a number to a string literal with constexpr

Variadic templates to the rescue. :)

namespace detail
{
template<unsigned... digits>
struct to_chars { static const char value[]; };

template<unsigned... digits>
constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0};

template<unsigned rem, unsigned... digits>
struct explode : explode<rem / 10, rem % 10, digits...> {};

template<unsigned... digits>
struct explode<0, digits...> : to_chars<digits...> {};
}

template<unsigned num>
struct num_to_string : detail::explode<num> {};

As always, here's a live example on Coliru showing usage and the (relevant) generated assembly.


It's straightforward to adapt this approach to support negative numbers as well. Here's a more generic form that requires the user to input the integer's type:

namespace detail
{
template<uint8_t... digits> struct positive_to_chars { static const char value[]; };
template<uint8_t... digits> constexpr char positive_to_chars<digits...>::value[] = {('0' + digits)..., 0};

template<uint8_t... digits> struct negative_to_chars { static const char value[]; };
template<uint8_t... digits> constexpr char negative_to_chars<digits...>::value[] = {'-', ('0' + digits)..., 0};

template<bool neg, uint8_t... digits>
struct to_chars : positive_to_chars<digits...> {};

template<uint8_t... digits>
struct to_chars<true, digits...> : negative_to_chars<digits...> {};

template<bool neg, uintmax_t rem, uint8_t... digits>
struct explode : explode<neg, rem / 10, rem % 10, digits...> {};

template<bool neg, uint8_t... digits>
struct explode<neg, 0, digits...> : to_chars<neg, digits...> {};

template<typename T>
constexpr uintmax_t cabs(T num) { return (num < 0) ? -num : num; }
}

template<typename Integer, Integer num>
struct string_from : detail::explode<(num < 0), detail::cabs(num)> {};

Its usage is like:

string_from<signed, -1>::value

as demonstrated in the live example on Coliru.

How do I convert a C string to a int at compile time?

Defining a constexpr stoi isn't too hard with regular C strings. It can be defined as follows:

constexpr bool is_digit(char c) {
return c <= '9' && c >= '0';
}

constexpr int stoi_impl(const char* str, int value = 0) {
return *str ?
is_digit(*str) ?
stoi_impl(str + 1, (*str - '0') + value * 10)
: throw "compile-time-error: not a digit"
: value;
}

constexpr int stoi(const char* str) {
return stoi_impl(str);
}

int main() {
static_assert(stoi("10") == 10, "...");
}

The throw expression is invalid when used in constant expressions so it'll trigger a compile time error rather than actually throwing.

Is it possible to generate a string at compile time?

After a bit more digging around I came across this on SO: https://stackoverflow.com/a/24000041/897778

Adapted for my use case I get:

namespace detail
{
template<unsigned... digits>
struct to_chars { static const char value[]; };

template<unsigned... digits>
const char to_chars<digits...>::value[] = {'{', ('0' + digits)..., '}' , 0};

template<unsigned rem, unsigned... digits>
struct explode : explode<rem / 10, rem % 10, digits...> {};

template<unsigned... digits>
struct explode<0, digits...> : to_chars<digits...> {};
}

template<unsigned num>
struct num_to_string : detail::explode<num / 10, num % 10>
{};

template <unsigned N>
void test()
{
const char* str = num_to_string<N>::value;
}

boost::mpl was also suggested, but this code seems simpler.

Easiest way to convert int to string in C++

C++11 introduces std::stoi (and variants for each numeric type) and std::to_string, the counterparts of the C atoi and itoa but expressed in term of std::string.

#include <string> 

std::string s = std::to_string(42);

is therefore the shortest way I can think of. You can even omit naming the type, using the auto keyword:

auto s = std::to_string(42);

Note: see [string.conversions] (21.5 in n3242)

Is it possible to perform a string to int mapping at compile time?

As @harmic has already mentioned in the comments, you should probably just pass the name to the constructor. This might also help reduce code bloat because you don't generate a new type for each function.

However, I don't want to miss the opportunity to show a dirty hack that might be useful in situations where the string cannot be passed to the constructor. If your strings have a maximum length that is known at compile-time, you can encode them into integers. In the following example, I'm only using a single integer which limits the maximum string length to 8 characters on my system. Extending the approach to multiple integers (with the splitting logic conveniently hidden by a small macro) is left as an exercise to the reader.

The code makes use of the C++14 feature to use arbitrary control structures in constexpr functions. In C++11, you'd have to write wrap as a slightly less straight-forward recursive function.

#include <climits>
#include <cstdint>
#include <cstdio>
#include <type_traits>

template <typename T = std::uintmax_t>
constexpr std::enable_if_t<std::is_integral<T>::value, T>
wrap(const char *const string) noexcept
{
constexpr auto N = sizeof(T);
T n {};
std::size_t i {};
while (string[i] && i < N)
n = (n << CHAR_BIT) | string[i++];
return (n << (N - i) * CHAR_BIT);
}

template <typename T>
std::enable_if_t<std::is_integral<T>::value>
unwrap(const T n, char *const buffer) noexcept
{
constexpr auto N = sizeof(T);
constexpr auto lastbyte = static_cast<char>(~0);
for (std::size_t i = 0UL; i < N; ++i)
buffer[i] = ((n >> (N - i - 1) * CHAR_BIT) & lastbyte);
buffer[N] = '\0';
}

template <std::uintmax_t Id>
struct Profile
{
char name[sizeof(std::uintmax_t) + 1];

Profile()
{
unwrap(Id, name);
std::printf("%-8s %s\n", "ENTER", name);
}

~Profile()
{
std::printf("%-8s %s\n", "EXIT", name);
}
};

It can be used like this:

void
function()
{
const Profile<wrap("function")> profiler {};
}

int
main()
{
const Profile<wrap("main")> profiler {};
function();
}

Output:

ENTER    main
ENTER function
EXIT function
EXIT main

How to convert an int to string in C?

EDIT: As pointed out in the comment, itoa() is not a standard, so better use sprintf() approach suggested in the rivaling answer!


You can use itoa() function to convert your integer value to a string.

Here is an example:

int num = 321;
char snum[5];

// convert 123 to string [buf]
itoa(num, snum, 10);

// print our string
printf("%s\n", snum);

If you want to output your structure into a file there is no need to convert any value beforehand. You can just use the printf format specification to indicate how to output your values and use any of the operators from printf family to output your data.



Related Topics



Leave a reply



Submit