類型安全且自動管理內存的返回 std::string 的 sprintf 實現

這篇博文裏,我提到了一個例子,說的是使用C++實現類型安全的printf。這個例子很驚豔,可是在我寫程序的時候,並不是那麼「迫切」地須要它出如今個人工具箱中,由於它並不比普通的printf方便,並且它沒有出現的標準庫中。因此本身也懶得整。相反,這個函數的兄弟,sprintf,卻是一個很是須要的函數。不單單是由於須要它類型安全,而是 sprintf 有比 printf 更多的麻煩:html

  1. 首先它確實也不是類型安全的
  2. 使用sprintf以前,必需要先準備一段buffer,但這個buffer的大小難以肯定,還要防溢出
  3. C++程序中多數時候我仍是須要使用 string,因而仍是要再用buffer中的字符串再生成一個string對象來使用。

這很是的麻煩,可是,sprintf 緊湊的表達是它最大的優勢~我纔不會用流去格式化一個字符串呢,實在是讓人精神分裂啊。安全

想到用 variadic template 實現的 printf 以後,以爲應該是徹底能夠模仿這個作一個 sprintf 函數的啊,除了獲得類型安全的好外以外,還能直接從函數返回字符串,又不會有溢出的問題,string和流自動地把內存管理好。一舉多得。編輯器

std::string sprintf(const char *s)
{
    std::stringstream ss;
    _sprintf(ss, s);
    return ss.str();
}

void _sprintf(std::stringstream & ss, const char *s)
{
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                throw std::runtime_error("invalid format string: missing arguments");
            }
        }
        ss << *s++;
    }
}

template<typename T,typename... Args>
std::string sprintf(const char *s, T value, Args... args) {
    std::stringstream ss;
    _sprintf(ss, s, value, args...);
    return ss.str();
}

template<typename T, typename... Args>
void _sprintf(std::stringstream & ss, const char *s, T value, Args... args)
{
    
    while (*s) {
        if (*s == '%') {
            if (*(s + 1) == '%') {
                ++s;
            }
            else {
                ss << value;
                _sprintf(ss, s + 1, args...); // call even when *s == 0 to detect extra arguments
                return;
            }
        }
        ss << *s++;
    }
    throw std::logic_error("extra arguments provided to lyw::sprintf");
}

使用起來也很方便,直接像原來的sprintf同樣用就行了。並且若是自定義的對象實現的流操做符重載,就能夠自動地與這個函數配合。很是精巧。ide

string str = sprintf("I tried % times in % ", 10, "Monday");

只須要加一點點代碼(使用C++的流控制符),就能夠實現各加精細的格式,這個版本我就再也不貼了。函數

PS: 代碼在VS2013上跑,很High,工具

      VS2013在C++的開發環境(我是指編輯器)上作了很大的改進,並且速度也快了。算是可貴spa

相關文章
相關標籤/搜索