惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
L
LangChain Blog
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
A
About on SuperTechFans
J
Java Code Geeks
量子位
博客园 - 三生石上(FineUI控件)
博客园 - Franky
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
IT之家
IT之家
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
雷峰网
雷峰网
D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
Martin Fowler
Martin Fowler
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

博客园 - bodong

Unreal Engine 5创建项目失败:文件“{ConfigFile}”处理失败。 如何找到当前计算机所有的UnrealEngine安装位置 Unreal Engine 5调试资源依赖来源的方法 GhostDoc Enterprise.v2024.2.24300 for Visual Studio 2022插件漏洞分析和离线解锁方法 ScyllaHide插件可能导致x64dbg的日志窗口无法捕获应用程序输出的日志字符串 C++使用宏来判断当前编译期支持的C++标准版本的方法 VisualStudio各版本_MSC_VER和_MSC_FULL_VER宏定义值列表 Rider如何针对一个项目禁用某一类错误检查(Inspections)的办法 UnrealEngine 5 C++标准版本 UnrealEngine Setup即使挂了代理也很慢的解决办法 Windows平台下使用C++和Windows API计算文件MD5的方法 Windows平台下使用C++检测硬盘分区是否属于SSD硬盘的方法 将C++代码文件路径、行号、函数名称等打包到#pragma message输出的方法 KnownDLLs列表 Avalonia实现Visual Studio风格标题栏的方法 解决IDA Pro Warning:mssdk64_win7: No such file or directory AHeadLib.Net C#版本的DLL劫持代码生成工具 安装Visual Studio 2022指定版本的方法 UnrealSharp: 一个可以让你在UnrealEngine5中采用C# 12和.NET 8.0开发的插件
CPPStringFormatting: 使用.NET格式化字符串风格格式化C++字...
bodong · 2024-06-24 · via 博客园 - bodong

详细信息和变更请参考github首页,变更、更新等不再同步到此页面。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这是一个使用C#格式化字符串风格来格式化C++字符串的库,它是类型安全的、多线程安全、可乱序、高效的的格式化库。
本项目支持几乎常见所有主流编译,无论有无C++ 11支持都可以。我使用Visual Studio 2008/2010/2022、CodeBlock with gcc、xcode 15对项目进行了测试,并通过了测试。

代码在github上:CPPStringFormatting

如何使用?

这是一个纯C++头文件项目,clone项目,然后将项目根目录添加到头文件搜索路径中并包含Format/Format.hpp即可。

如何测试? 

请先Clone本项目,然后使用CMake生成项目即可测试。 需要注意的一点是本项目依赖于googletests,所以你在clone之后,需要使用如下指令确保googletests的submodule正确被拉取下来了,否则CMake生成项目时会出错。

git submodule init
git submodule update 

另外如果你想要测试的C++编译器版本很早,比如不支持C++ 11的Visual Studio 2008,那么你可能需要手动将googletests切换到更早的tag上,比如1.8.x

生成项目后,使用IDE打开生成的项目文件即可,编译和运行UniTests项目即可开始测试。UnitTests_Format.cpp中代码即是针对std::basic_string<TCharType>的使用案例。你可以参考StandardLibraryAdapter.hpp的代码为你自己的字符串类添加适配器,即可让这套系统配合你的字符串类进行工作。

代码片段 

这里简单展示一下代码风格和用法,你可以在UnitTests_Format.cpp中找到更多用法和案例。

#include <Format/Format.hpp>
#include <Format/StandardLibraryAdapter.hpp>

using namespace Formatting;

TEST(Format, STL_Char_Format)
{
    const std::string i0 = "Hello CppMiniToolkit";
    std::string r0 = StandardLibrary::Format(i0.c_str());
    EXPECT_EQ(r0, i0);

    const std::string i1 = "Hello CppMiniToolkit {0}";
    std::string r1 = StandardLibrary::Format(i1.c_str(), 1024);
    EXPECT_EQ(r1, "Hello CppMiniToolkit 1024");

    const std::string r2 = StandardLibrary::Format("{0}--#--{1,8}--#--{2}", 100, -40.2f, " String ");
    EXPECT_EQ(r2, "100--#--  -40.20--#-- String ");

    const std::string r3 = StandardLibrary::Format("{0}--#--{1,8}--#--{1}", 100, -40.2f);
    EXPECT_EQ(r3, "100--#--  -40.20--#---40.20");

    const std::string r4 = StandardLibrary::Format("{0}--#--{1,8}--#--{3}", 100, -40.2f, std::string("xxx"));
    EXPECT_EQ(r4, "100--#--  -40.20--#--{3}");

    const std::string r5 = StandardLibrary::Format("{0}", char('c'), short(2));
    EXPECT_EQ(r5, "c");

    const std::string r6 = StandardLibrary::Format("0x{0:x}", 100, (unsigned long)(100));
    EXPECT_EQ(r6, "0x64");

    // gen compile error
    //StandardLibrary::Format("{0}", std::wstring(L"x"));
}

TEST(Format, STL_WChar_Format)
{   
    const std::wstring r7 = StandardLibrary::Format(L"Test{1}, {2:f4}, {0}, {0,4}", L" X ", 20, -10.005f);
    EXPECT_EQ(r7, L"Test20, -10.0050,  X ,   X ");

    const std::wstring r8 = StandardLibrary::Format(L"Test{1}, {2:f4}, {0}, {0,4}");
    EXPECT_EQ(r8, L"Test{1}, {2:f4}, {0}, {0,4}");

    const std::wstring r9 = StandardLibrary::Format(std::wstring(L"Test{1}, {2:f4}, {0}, {0,4}"), L" X ", 20, -10.005f);
    EXPECT_EQ(r9, L"Test20, -10.0050,  X ,   X ");

    const std::wstring r11 = StandardLibrary::Format(L"\u4F60\u597D : {0}", L"\u4E2D\u6587");
    EXPECT_EQ(r11, L"\u4F60\u597D : \u4E2D\u6587");
}

TEST(Format, STL_WChar_FormatTo)
{
    std::wstring v;

    StandardLibrary::FormatTo(v, L"Test{1}, {2:f4}, {0}, {0,4}", L" X ", 20, -10.005f);
    EXPECT_EQ(v, L"Test20, -10.0050,  X ,   X ");

    // test invalid param
    StandardLibrary::FormatTo(v, L"Test{1}, {2:f4}, {0}, {0,4}");
    EXPECT_EQ(v, L"Test{1}, {2:f4}, {0}, {0,4}");

    StandardLibrary::FormatTo(v, std::wstring(L"Test{1}, {2:f4}, {0}, {0,4}"), L" X ", 20, -10.005f);
    EXPECT_EQ(v, L"Test20, -10.0050,  X ,   X ");

    StandardLibrary::FormatTo(v, L"\u4F60\u597D : {0}", L"\u4E2D\u6587");
    EXPECT_EQ(v, L"\u4F60\u597D : \u4E2D\u6587");
}
 

如何集成? 

想要将格式化库适配你自己的字符串类或者使用你自己的容器类来接管格式化库内部的容器,那么你只需要做三件事:

  • 第一,实现自己的Policy类,这个类需要告知框架必须要的基础类型都是什么,这通过typedef来实现;并且你还需要实现几个基础接口即可:FindByHashKeyReserveListAppendPattern
  • 第二,你需要在命名空间Formatting::Shims下实现两个垫片函数PtrOfLengthOf,这两个函数是针对你的字符串类的重载。
  • 第三,你需要实现自己的Format函数。如果你使用C++ 11或更新标准,那么你只需要使用不定参数模板即可;如果你要支持C++ 11更早的版本,你需要用到一些宏技巧来生成支持多个参数的Format函数。
    你可以参考UnitTests/Sources/MFCAdapter.hpp的源代码,这里展示了如何为MFC字符串提供Format支持。当然也可以参考Format/StandardLibraryAdapter.hpp,这里是针对stl::basic_string<TCharType>的适配代码。
    通过这几个简单的适配器,你就可以将这个字符串格式化库用到你自己的类型上了。

如何扩展?

CPPStringFormatting本身是类型安全的系统,如果你尝试将不支持的类型提交给格式化函数,那么你将会在编译阶段得到一个编译错误。要解决这个错误,你有两种办法:

  • 第一,将参数转换成受支持的类型。
  • 第二,提供一个针对该类型的自定义Translator,这样框架将能自动帮你完成转换操作。 你可以在UnitTests/Sources/UnityTests_Extends.cpp中找到扩展的方法和举例。
// The following class provides automatic conversion capabilities for Vector3, so that Vector3 type parameters can be formatted directly.
namespace Formatting
{
    namespace Details
    {
        // convert Vector3 to string
        template <>
        class TTranslator< char, Vector3 > :
            public TTranslatorBase< char, Vector3 >
        {
        public:
            typedef TTranslatorBase< char, Vector3 >           Super;
            typedef Super::CharType                            CharType;
            typedef Super::FormatPattern                       FormatPattern;
            typedef Super::ByteType                            ByteType;
            typedef Super::SizeType                            SizeType;
            typedef Super::StringType                          StringType;
            typedef Super::CharTraits                          CharTraits;

            static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg)
            {
                std::string text = arg.ToString();

                Super::AppendString(strRef, pattern, text.c_str(), text.size());

                return true;
            }
        };

        // convert Vector3 to wstring
        template <>
        class TTranslator< wchar_t, Vector3 > :
            public TTranslatorBase< wchar_t, Vector3 >
        {
        public:
            typedef TTranslatorBase< wchar_t, Vector3 >        Super;
            typedef Super::CharType                            CharType;
            typedef Super::FormatPattern                       FormatPattern;
            typedef Super::ByteType                            ByteType;
            typedef Super::SizeType                            SizeType;
            typedef Super::StringType                          StringType;
            typedef Super::CharTraits                          CharTraits;

            static bool Transfer(StringType& strRef, const FormatPattern& pattern, const Vector3& arg)
            {
                std::wstring text = arg.ToWString();

                Super::AppendString(strRef, pattern, text.c_str(), text.size());

                return true;
            }
        };
    }
}

差异 Difference

虽然CPPStringFormatting支持了大部分常见的格式化需求,但并也有许多C#格式化的功能尚不支持。具体C#支持的格式化规范请参考这里: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings

TypeStatus
B
C
D
E
F
G
N
P
R
X
Percision
Width

遇到不支持的格式标志符时C#会抛出异常,CPPStringFormatting会直接输出格式符字符串,一些情况会触发assert或忽略。因此当你遇到与预期不一致的问题时,请检查格式标志符是否正确,或者是否使用了不被支持的标志符。