如何在 clang++ 中干净地进行格式化字符串连接

How to cleanly do formatted string concatenation in clang++

我在 Windows 上使用 clang++ 来做一些非常基本的 SDL2 东西,但我刚发现 <format> 没有附带 Clang++,也没有“fmt"开箱即用。

我需要的是一种更漂亮的方法来连接一堆格式化的字符串,这在其他地方是微不足道的,虽然我设法让它工作,但我对长时间重复的结构不满意。这是我得到的:

std::string getInfo() {
    ...
    char ver[200] = "", verInfo[200] = "";
    sprintf (ver, "Compiled using SDL version  : %d.%d.%d \n", compiled.major, compiled.minor, compiled.patch); strcat_s (verInfo, ver);
    sprintf (ver, "and linked with SDL version : %d.%d.%d \n", linked.major, linked.minor, linked.patch);       strcat_s (verInfo, ver);
    sprintf (ver, "and using SDL_TTF version   : %d.%d.%d \n", ttfv.major, ttfv.minor, ttfv.patch);             strcat_s (verInfo, ver);
    return verInfo;
}

问:是否有更紧凑、更智能的方法,或许不使用 2 个缓冲区来做到这一点?


更新:

按照用户 Galik 的建议,通过在流处理程序中使用自定义结构,设法显着减少流代码。代码现在看起来像这样:

struct version_info {
    int major, minor, patch;
    friend std::ostream& operator<<(std::ostream& os, version_info const& v) {
        os << v.major << '.' << v.minor << '.' << v.patch;
        return os;
    }
};

std::string getInfo() {    
    SDL_version aa;
    SDL_version bb;
    SDL_version cc;
    SDL_VERSION(&aa);
    SDL_GetVersion(&bb);
    SDL_TTF_VERSION(&cc);

    std::ostringstream oss;
    oss << "SDL version  : " << aa << '\n';
    oss << "SDL linker   : " << bb << '\n';
    oss << "SDL_TTF ver. : " << cc << '\n';
    return oss.str();
}

...但我现在遇到编译错误。


更新 2

使用inline代替结构,解决问题:

inline std::ostream& operator<<(std::ostream& os, SDL_version const& v) {
    os << int(v.major) << '.' << int(v.minor) << '.' << int(v.patch);
    return os;
}
...
// same code

这样的事情可以接受吗?

// Old Way
std::string getInfo() {

    char ver[200] = "", verInfo[200] = "";
    sprintf (ver, "Compiled using SDL version  : %d.%d.%d \n", compiled.major, compiled.minor, compiled.patch); strcat (verInfo, ver);
    sprintf (ver, "and linked with SDL version : %d.%d.%d \n", linked.major, linked.minor, linked.patch);       strcat (verInfo, ver);
    sprintf (ver, "and using SDL_TTF version   : %d.%d.%d \n", ttfv.major, ttfv.minor, ttfv.patch);             strcat (verInfo, ver);
    return verInfo;
}

// New Way
std::string getInfo2() {

    std::ostringstream oss;
    oss << "Compiled using SDL version  : " << compiled.major << '.' << compiled.minor << '.' << compiled.patch << '\n';
    oss << "and linked with SDL version : " << linked.major   << '.' << linked.minor   << '.' << linked.patch   << '\n';
    oss << "and using SDL_TTF version   : " << ttfv.major     << '.' << ttfv.minor     << '.' << ttfv.patch     << '\n';

    return oss.str();
}

如果紧凑是您的目标,您可以通过为版本信息添加一个输出运算符友元函数来变得更聪明。

可能是这样的:

struct version_info
{
    int major;
    int minor;
    int patch;

    // formatting function for version_info objects.
    friend std::ostream& operator<<(std::ostream& os, version_info const& v)
    {
        os << v.major << '.' << v.minor << '.' << v.patch;
        return os;
    }
};

// ... a few bytes later ...

std::string getInfo3() {

    std::ostringstream oss;
    oss << "Compiled using SDL version  : " << compiled << '\n';
    oss << "and linked with SDL version : " << linked   << '\n';
    oss << "and using SDL_TTF version   : " << ttfv     << '\n';

    return oss.str();
}

对于格式化函数,术语friend用于使函数位于结构外部,因此它不是 成员函数 尽管它是在 struct 定义中声明的。

格式化函数参数是一个std::ostream& os和一个用于打印的结构类型的对象。在这种情况下 version_info。所以它需要一个你类型的对象 version_info 并将它打印到 os.

每当你有命令 std::cout << compiled; 时,编译器都会用调用 格式化函数 来替换它,如下所示:

version_info compiled {2, 1, 0};

std::cout << compiled;

...变成...

version_info compiled {2, 1, 0};

operator<<(std::cout, compiled); // a call to your formatting function

但是如果您没有创建结构怎么办?

如果您使用来自其他库的版本信息,例如 SDL,您将必须创建 输出函数 作为内联自由函数,如下所示:

#include <SDL2/SDL_version.h>

// ... a few bytes later ...

inline std::ostream& operator<<(std::ostream& os, SDL_version const& v)
{
    os << int(v.major) << '.' << int(v.minor) << '.' << int(v.patch);
    return os;
}

// ... a few bytes later ...

int main()
{
    SDL_version compiled {2, 1, 0};

    std::cout << compiled;
}