Created
June 24, 2017 08:17
-
-
Save jgcoded/f3c0052778296c9ae1b50fa30dfd6d9f to your computer and use it in GitHub Desktop.
C++ Compile time conversion of a number to a string
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
This file contains code to convert unsigned integers to strings | |
at compile-time, and then modify those strings include more | |
information. | |
One use case is to convert library MAJOR, MINOR, and PATCH | |
numbers into a custom string (see below). | |
Other types like signed integers and floats can be added | |
with template specializition on the converter struct. | |
Inspiration for this code came from these two stackoverflow posts: | |
https://stackoverflow.com/questions/6713420/c-convert-integer-to-string-at-compile-time | |
https://stackoverflow.com/questions/26568920/how-do-you-implement-compile-time-string-conversion-functions | |
*/ | |
/*! | |
Returns the number of digits in n | |
*/ | |
constexpr std::size_t num_digits(std::size_t n) | |
{ | |
return n < 10 ? 1 : num_digits(n / 10) + 1; | |
} | |
/*! | |
Type container of characters. All of the characters that make | |
up the string are stored in the template parameters. | |
The str member variable will then be a compile-time | |
created string consisting of the characters in the | |
template parameter pack. | |
*/ | |
template<char... Chars> | |
struct char_list { | |
const char str[sizeof...(Chars)] = { Chars... }; | |
}; | |
/*! | |
Partial template specialization to append characters | |
onto the template paramters of a char_list | |
*/ | |
template<class List, char C> | |
struct append_char; | |
template<template<char...> class List, char... Cs, char C> | |
struct append_char<List<Cs...>, C> { | |
using type = char_list<Cs..., C>; | |
}; | |
/*! | |
Partial template specialization to combine the parameters | |
in two char_lists into one. | |
*/ | |
template< class List1, class List2> | |
struct combine_strings; | |
template<template<char...> class List1, char... Cs1, | |
template<char...> class List2, char... Cs2> | |
struct combine_strings<List1<Cs1...>, List2<Cs2...>> { | |
using type = char_list<Cs1..., Cs2...>; | |
}; | |
/*! | |
This struct takes an unsigned integer as a template argument | |
and separates the digits in the unsigned integer into | |
separate characters. | |
*/ | |
template<std::size_t D, std::size_t N, char... Chars> | |
struct converter { | |
using type = typename converter<D - 1, N / 10, '0' + N % 10, Chars...>::type; | |
}; | |
/*! | |
Base case for integer conversion: Only 1 digit left in the | |
unsigned integer; | |
*/ | |
template<std::size_t N, char... Chars> | |
struct converter<1, N, Chars...> { | |
using type = char_list<'0' + N, Chars...>; | |
}; | |
/* Convenience using alias templates */ | |
template<class List, char C> | |
using append_char_t = typename append_char<List, C>::type; | |
template< class List1, class List2> | |
using combine_strings_t = typename combine_strings<List1, List2>::type; | |
template<std::size_t N> | |
using convert_t = typename converter<numDigits(N), N>::type; | |
/*! | |
Convenience function that's meant to be given a | |
char_list as a template argument; | |
*/ | |
template<typename List> | |
static constexpr const char* make_string() { | |
static List charList; | |
return charList.str; | |
} | |
/*! | |
Example of how a custom version string could be produced. | |
This function builds a string at compile time using three | |
integers defined in the MyLib_VERSION_* macros. | |
\return "0.1.0" | |
*/ | |
const char* GetVersionString() | |
{ | |
using major = append_char_t<convert_t<MyLib_VERSION_MAJOR>, '.'>; | |
using minor = append_char_t<convert_t<MyLib_VERSION_MINOR>, '.'>; | |
using patch = convert_t<MyLib_VERSION_PATCH>; | |
using VersionString = combine_strings_t<combine_strings_t<major, minor>, patch>; | |
return make_string<VersionString>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment