Skip to content

Instantly share code, notes, and snippets.

@Harold2017
Forked from jgcoded/compiletime-strings.hpp
Last active December 22, 2021 09:46
Show Gist options
  • Save Harold2017/38bbdc8e52275ef75862688ae3db4252 to your computer and use it in GitHub Desktop.
Save Harold2017/38bbdc8e52275ef75862688ae3db4252 to your computer and use it in GitHub Desktop.
C++ Compile time conversion of a number to a string
/*!
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<num_digits(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