I wrote a small header that is supposed to take care of calling the right strtoX
or or stoX
function for me and doing so at compile time. The code contains two functions: num_cast
and string_cast
. Each returns a number or string from the argument specified. See main
for an example on how to use this.
I'm looking for reviews on the code design/quality and to possibly illuminate a clearer or more concise way of doing this.
#include <string>
#include <iostream>
#include <assert.h>
#include <type_traits>
#include <cstdlib>
template <class T, int B>
constexpr bool string_util_castable()
{
return (std::is_same<T, int>::value ||
std::is_same<T, long>::value ||
std::is_same<T, unsigned long>::value ||
std::is_same<T, unsigned long long>::value ||
std::is_same<T, long long>::value ||
std::is_same<T, float>::value ||
std::is_same<T, double>::value ||
std::is_same<T, long double>::value ||
std::is_same<T, unsigned>::value) &&
(((B & (B - 1)) == 0 || B == 10) && B >= 0);
}
// overloaded to avoid having a call to std::string constructor and creating
// a std::string for no reason. This consequently means using the heap when
// it isn't necessary to do so.
template <class T, int B = 10>
T num_cast(const char *param)
{
static_assert(string_util_castable<T, B>(), "Type and/or base is not supported in num_cast!");
if (std::is_same<T, int>::value) {
return static_cast<int>(std::strtol(param, nullptr, B));
}
if (std::is_same<T, long>::value) {
return std::strtol(param, nullptr, B);
}
if (std::is_same<T, unsigned long>::value) {
return std::strtoul(param, nullptr, B);
}
if (std::is_same<T, unsigned long long>::value) {
return std::strtoull(param, nullptr, B);
}
if (std::is_same<T, long long>::value) {
return std::strtoll(param, nullptr, B);
}
if (std::is_same<T, float>::value) {
return std::strtof(param, nullptr);
}
if (std::is_same<T, double>::value) {
return std::strtod(param, nullptr);
}
if (std::is_same<T, long double>::value) {
return std::strtold(param, nullptr);
}
if (std::is_same<T, unsigned>::value) {
return static_cast<unsigned>(std::strtoul(param, nullptr, B));
}
}
template <class T, int B = 10>
T num_cast(const std::string& param)
{
static_assert(string_util_castable<T, B>(), "Type and/or base is not supported in num_cast!");
if (std::is_same<T, int>::value) {
return std::stoi(param, nullptr, B);
}
if (std::is_same<T, long>::value) {
return std::stol(param, nullptr, B);
}
if (std::is_same<T, unsigned long>::value) {
return std::stoul(param, nullptr, B);
}
if (std::is_same<T, unsigned long long>::value) {
return std::stoll(param, nullptr, B);
}
if (std::is_same<T, long long>::value) {
return std::stoll(param, nullptr, B);
}
if (std::is_same<T, float>::value) {
return std::stof(param, nullptr);
}
if (std::is_same<T, double>::value) {
return std::stod(param, nullptr);
}
if (std::is_same<T, long double>::value) {
return std::stold(param, nullptr);
}
if (std::is_same<T, unsigned>::value) {
return static_cast<unsigned>(std::stoul(param, nullptr, B));
}
}
template <class T>
std::string string_cast(const T& num)
{
static_assert(string_util_castable<T, 10>(), "Type and/or base is not supported in string_cast!");
return std::to_string(num);
}
int main()
{
auto n = num_cast<signed>("-5");
auto s = string_cast(10);
auto ss = num_cast<int>(std::string("100"));
std::cout << n << "\n";
std::cout << s << "\n";
std::cout << ss << "\n";
}