Skip to content
sczybt edited this page Oct 9, 2014 · 6 revisions

Welcome to the CPPFormatLibrary wiki!

this simple project is a C++ format library, it use .net style to format string. if you have used C++/CLI or C#, you will be very familiar with this code:

string str;
StandardLibrary::FormatTo(str, "{0}--#--{1,8}--#--{2}", 100, -40.2f, " String ");

and then, str is:

100--#--  -40.20--#-- String

this library is a cross-platform libary, i have compiled it under windows, macos, linux with msvc, g++ and xcode darwin C++ with c++ 11 support or with out c++ 11 support. and in most case, it will be 2x fast than c sprintf and it is thread safe.

this project design like boost, it is a header only project. if you want to use it, you only have to include the main Format.hpp.

this is a test useage:

// Test
#include "Format.hpp"

#include "Format/ProgressTimer.hpp"

#define TEST_PERFORMANCE_IN_TOOLS 0

using namespace FormatLibrary;

#include <iostream>
#include <vector>
using namespace std;

void TestProfile()
{
    const int TEST_COUNT = 100000;

    {
        Profile::ProgressTimer Timer("FL");

        for (int i = 0; i < TEST_COUNT; ++i)
        {
            string str;
            StandardLibrary::FormatTo(str, "{0}--#--{1,8}--#--{2}", 100, -40.2f, " String ");
            StandardLibrary::FormatTo(str, "{0}--#--{1,8}--#--{1}", 100, -40.2f);
            StandardLibrary::FormatTo(str, "{0}--#--{1,8}--#--{3}", 100, -40.2f, std::string("xxx"));
        }
    }

#if !FL_COMPILER_MSVC
#define sprintf_s sprintf
#endif

#if !TEST_PERFORMANCE_IN_TOOLS
    {
        Profile::ProgressTimer Timer("CL");

        for (int i = 0; i < TEST_COUNT; ++i)
        {
            string str;
            char szBuf[64];
            sprintf_s(szBuf, "%d--#--%8.2f--#--%s", 100, -40.2f, " String ");
            str = szBuf;
            sprintf_s(szBuf, "%d--#--%8.2f--#--%f", 100, -40.2f, 0.0f);
            str = szBuf;
            sprintf_s(szBuf, "%d--#--%8.2f--#--%%f", 100, -40.2f);
            str = szBuf;
        }
    }
#endif
}

#if FL_PLATFORM_HAS_CPP11 && (FL_COMPILER_MSVC||FL_PLATFORM_MACOS)
#include <thread>

void TestProfileMultiThread()
{
    std::thread t0( TestProfile );
    std::thread t1( TestProfile );
    std::thread t2( TestProfile );

    t0.join();
    t1.join();
    t2.join();
}
#endif

int main()
{
    StandardLibrary::STLGlobalPatternStorageA Storage;
    Utility::TAutoString<char> TestStr;

    const char* pszTest = "{0},xxxd{1:d2}={2,3:d2} !! {{}} {0,-5:d8}";
    Storage.LookupPatterns(pszTest, strlen(pszTest));

    std::string str;
    StandardLibrary::FormatTo(str, "test{0}", 10);

    StandardLibrary::FormatTo(str, "{0}", char('c'), short(2));

#if FL_COMPILER_MSVC
    StandardLibrary::FormatTo(str, "0x{0:x}", 100, DWORD(100));
#endif

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

    cout << str << endl;
    wcout << wstr << endl;

    TestProfile();
    
#if FL_PLATFORM_HAS_CPP11 && (FL_COMPILER_MSVC||FL_PLATFORM_MACOS)
    TestProfileMultiThread();
#endif

    return 0;
}

the release output:

0x64
Test20, -10.0050,  X ,  X
0x64
Test20, -10.0050,  X ,  X
1920 FLElapse:0.0762746
1920 CLElapse:0.269722
1636 FLElapse:0.0756153
7732 FLElapse:0.0766446
7956 FLElapse:0.0762051
7956 CLElapse:0.285714
1636 CLElapse:0.288648
7732 CLElapse:0.289193

this project can be integrated to your project very easy, this is an example demonstrate that how to integrate it to Unreal Engine 3/4.

// File : UnFormat.h

// created by bodong
#pragma once

#include <Format.hpp>

#include "Map.h"

class FUnrealFormatPolicy
{
public:
    typedef TCHAR                                                               CharType;
    typedef FormatLibrary::Algorithm::TFormatPattern<CharType>                  FormatPattern;
    typedef FormatPattern::SizeType                                             SizeType;
    typedef FormatPattern::ByteType                                             ByteType;
    typedef FormatLibrary::Utility::TAutoArray<FormatPattern,0xF,0>             PatternListType;
    typedef PatternListType::ConstIterator                                      PatternIterator;
    typedef TMap<SizeType, PatternListType>                                     PatternMapType; 

#if FL_WITH_THREAD_LOCAL || !FL_WITH_MULTITHREAD
    typedef FormatLibrary::Utility::CriticalSectionNone                         CriticalSectionType;
#else
    typedef FormatLibrary::System::CriticalSection                              CriticalSectionType;
#endif

    typedef FormatLibrary::StandardLibrary::TDefaultStringHasher<CharType>      HasherType;
    typedef std::runtime_error                                                  ExceptionType;

    static const PatternListType* FindByHashKey(const PatternMapType& Storage, SizeType HashKey)
    {        
        return Storage.Find(HashKey);
    }

    static void ReserveList(PatternListType& /*ListRef*/, int /*Len*/ )
    {
        // AtuoArray does not need reserve
    }

    static const PatternListType* Emplace(
        PatternMapType& Storage,
        SizeType HashKey,
#if FL_PLATFORM_HAS_RIGHT_VALUE_REFERENCE
        PatternListType&& Patterns
#else
        const PatternListType& Patterns
#endif
        )
    {       
        return &Storage.Set( HashKey, MOVE_SEMANTIC(Patterns) );
    }

    static void AppendPattern(PatternListType& Patterns, const FormatPattern& Pattern )
    {
        Patterns.AddItem(Pattern);
    }
};

typedef FormatLibrary::Algorithm::TGlobalPatternStorage< FUnrealFormatPolicy > FGlobalPatternStorage;

#define _UE_FL_TEMPLATE_PARAMETERS_( d, i ) \
    FL_PP_COMMA_IF(i) typename FL_PP_CAT(T, i)

#define _UE_FL_TEMPLATE_AGUMENTS_( d, i ) \
    FL_PP_COMMA_IF(i) FL_PP_CAT(T, i)

#define _UE_FL_FORMAL_AGUMENTS_( d, i ) \
    FL_PP_COMMA_IF(i) const FL_PP_CAT(T, i)& FL_PP_CAT(Arg, i)

#define _UE_FL_REAL_AGUMENTS_( d, i ) \
    FL_PP_COMMA_IF(i) FL_PP_CAT(Arg, i)

// make a export macro for FString
#define _UE_FL_EXPORT_FOR_STRING( i ) \
    template < FL_PP_REPEAT(i, _UE_FL_TEMPLATE_PARAMETERS_, ) > \
    static FString FormatTo(const TCHAR* Format, FL_PP_REPEAT(i, _UE_FL_FORMAL_AGUMENTS_, )) \
    { \
        check(Format); \
        FormatLibrary::Utility::TAutoString<TCHAR> Results; \
        FormatLibrary::Detail::FormatTo< TCHAR, FGlobalPatternStorage, FL_PP_REPEAT(i, _UE_FL_TEMPLATE_AGUMENTS_, )>(Results, Format, FL_PP_REPEAT(i, _UE_FL_REAL_AGUMENTS_, )); \
        FString Str( Results.GetLength(), Results.CStr() ); \
        return Str; \
    } \
    template < FL_PP_REPEAT(i, _UE_FL_TEMPLATE_PARAMETERS_, ) > \
    FString& Format( const TCHAR* Format, FL_PP_REPEAT(i, _UE_FL_FORMAL_AGUMENTS_, ) ) \
    { \
        *this = FL_MOVE_SEMANTIC( FString::FormatTo<FL_PP_REPEAT(i, _UE_FL_TEMPLATE_AGUMENTS_, )>( Format, FL_PP_REPEAT(i, _UE_FL_REAL_AGUMENTS_, ) ) ); \
        return *this; \
    }

#define _IMPLEMENT_FORMAT_FOR_FSTRING() \
    FL_ENUM_SCALARS(_UE_FL_EXPORT_FOR_STRING)


// File: UnFormatPost.h

// add by bodong
#pragma once

namespace FormatLibrary
{
    template <>
    class TTranslator< TCHAR, FString > :
        public TTranslatorBase< TCHAR, FString >
    {
    public:
        typedef TTranslatorBase< TCHAR, FString >  Super;

        static bool Transfer( Super::StringType& S, const Super::FormatPattern& /*Pattern*/, const FString& Arg)
        {
            if( !Arg.IsEmpty() )
            {
                S.AddStr( *Arg, *Arg + Arg.Len() );    
            }
            
            return true;
        }
    };

    template <>
    class TTranslator< TCHAR, FName > :
        public TTranslatorBase< TCHAR, FName >
    {
    public:
        typedef TTranslatorBase< TCHAR, FName >  Super;

        static bool Transfer( Super::StringType& S, const Super::FormatPattern& /*Pattern*/, const FName& Arg)
        {
            FString Temp;
            Arg.ToString(Temp);
            if( !Temp.IsEmpty() )
            {
                S.AddStr( *Temp, *Temp + Temp.Len() );                
            }

            return true;
        }
    };
}
// File: Readmet.txt
1.add UnFormat.h and UnFormatPost.h to Core/Inc
2.add #include "UnFormat.h" to the beginnng of UnString.h
3.add _IMPLEMENT_FORMAT_FOR_FSTRING(); to the public area for FString class content
4.add #include "UnFormatPost.h" after UnName.h
5.enjoy it!!

and then in UnrealEngine, FString have a static member function FormatTo and a normal member function Format, it can be used to format all kinds of types to FString linke FString::Printf.

in the main project, there is a integration of std::string and std::wstring, you can see it. enjoy this library. thank you very much.

Clone this wiki locally