关于C++字符串格式化,其实是我在用C++输出时间字符串时想用到的,格式化有时候是个很方便的东西,但是找着网上也没有什么好用的方法,所以就自己实现了一个。时间复杂度为O(n),基本就是遍历一遍待格式化的字符串。
还有,代码未经优化,先看着吧,有空再优化。为了解决变长模板参数就已经绞尽脑汁了,再弄下去头发都掉光了。
C++标准:C++17以上
#ifndef __FORMAT_H__
#define __FORMAT_H__
#include <string>
#include <sstream>
#include <iomanip>
#if __cplusplus >= 201703L
#define CONSTEXPR constexpr
#else
#define CONSTEXPR
#endif
namespace __m_container
{
template<typename ... Args>
class Tuple;
template<>
class Tuple<> { };
template<typename First, typename ... Others>
class Tuple<First, Others...> : private Tuple<Others...>
{
public:
using parent = Tuple<Others...>;
Tuple(const First &f, const Others & ... o) : _M_value(f), parent(o...) { }
const First& get() const { return _M_value; }
const parent& next() const { return *this; }
protected:
const First &_M_value;
};
}
namespace __m_type_traits
{
template<typename>
struct array
{
};
template<typename T, std::size_t s>
struct array<T[s]>
{
using type = T;
};
template<std::size_t T>
struct __BaseDataType
{
using type = char[];
using utype = unsigned char[];
};
template<>
struct __BaseDataType<4>
{
using type = int;
using utype = unsigned int;
};
template<>
struct __BaseDataType<8>
{
using type = long long;
using utype = unsigned long long;
};
}
template<typename T>
static void __format(std::stringstream &ss, const char *ch, const T &arg)
{
using r_const_type = typename std::remove_const<T>::type;
using arg_type = typename std::remove_reference<r_const_type>::type;
if CONSTEXPR (std::is_arithmetic<arg_type>::value)
{
switch(*ch)
{
case 'd': ;
case 'i': ss << static_cast<long long>(arg); break;
case 'u': ss << static_cast<unsigned long long>(arg); break;
case 'o': ss << std::oct << static_cast<unsigned long long>(arg) << std::defaultfloat; break;
case 'x': ss << std::hex << "0x" << static_cast<unsigned long long>(arg) << std::defaultfloat; break;
case 'X': ss << std::setiosflags(std::ios::uppercase) << std::hex << "0X" << static_cast<unsigned long long>(arg) << std::defaultfloat; break;
case 'f': ss << arg; break;
case 'F': ss << std::setiosflags(std::ios::uppercase) << arg << std::defaultfloat; break;
case 'e': ss << std::scientific << arg << std::defaultfloat; break;
case 'E': ss << std::setiosflags(std::ios::uppercase) << std::scientific << arg << std::defaultfloat; break;
case 'g': ss << arg; break;
case 'G': ss << std::setiosflags(std::ios::uppercase) << arg << std::defaultfloat; break;
case 'a': ss << std::hexfloat << arg << std::defaultfloat; break;
case 'A': ss << std::setiosflags(std::ios::uppercase) << std::hexfloat << arg << std::defaultfloat; break;
}
}
else if CONSTEXPR (std::is_same<char *, r_const_type>::value ||
std::is_same<char, r_const_type>::value ||
std::is_same<std::string, r_const_type>::value ||
(std::is_array<arg_type>::value &&
std::is_same<char, typename __m_type_traits::array<arg_type>::type>::value))
{
if(*ch == 's')
{
ss << arg;
}
}
else if CONSTEXPR (std::is_pointer<arg_type>::value)
{
if(*ch == 'p')
{
ss << "0x" << std::hex << reinterpret_cast<__m_type_traits::__BaseDataType<sizeof(void *)>::utype>(arg) << std::defaultfloat;
}
}
else if CONSTEXPR (std::is_same<char, arg_type>::value)
{
if(*ch == 'c')
{
ss << arg;
}
}
}
static void format(std::stringstream &ss, const char *ch, const char *end)
{
while(ch != end)
{
ss << *ch++;
}
}
template<typename Arg>
static void format(std::stringstream &ss, const char *ch, const char *end, const __m_container::Tuple<Arg> &arg_list)
{
while(ch != end)
{
if(*ch == '%' && ch != end - 1)
{
if(*(ch + 1) == '%')
{
ss << '%';
ch += 2;
}
else
{
__format(ss, ++ch, arg_list.get());
format(ss, ++ch, end);
break;
}
}
else
{
ss << *ch++;
}
}
}
template<typename ... Args>
static void format(std::stringstream &ss, const char *ch, const char *end, const __m_container::Tuple<Args...> &arg_list)
{
while(ch != end)
{
if(*ch == '%' && ch != end - 1)
{
if(*(ch + 1) == '%')
{
ss << '%';
ch += 2;
}
else
{
__format(ss, ++ch, arg_list.get());
format(ss, ++ch, end, arg_list.next());
break;
}
}
else
{
ss << *ch++;
}
}
}
template<typename ... Args>
std::string format(std::string input, const Args & ... args)
{
std::stringstream ss;
__m_container::Tuple<Args...> arg_list(args...);
const char *begin = input.data(), *end = begin + input.size();
format(ss, begin, end, arg_list);
return ss.str();
}
#endif // __FORMAT_H__
关于Tuple成员使用引用类型,是为了避免复制;成员不用指针,是为了避免管理指针。使用引用类型的话,可以让编译器来管理空间。下面看看测试结果吧!!
测试代码:
#include <iostream>
int main()
{
std::cout << format("---0xff123: %x, 0.11001000: %E", 0xff123, 0.11001000) << std::endl;
std::cout << format("%d-%d-%d %d:%d:%d", 2019, 12, 29, 15, 52, 0) << std::endl;
std::cout << format("%d-%d-%d %s emmmm", 2019, 12, 29, "big success") << std::endl;
return 0;
}
运行结果:
有问题欢迎指出