C++字符串格式化的实现

关于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;
}

运行结果:

有问题欢迎指出

发布了19 篇原创文章 · 获赞 1 · 访问量 2779

猜你喜欢

转载自blog.csdn.net/qq811299838/article/details/103745767