-1

I'm considering transitioning from snprintf and its ilk to fmtlib. Imagine this fictional code:

class CFoo
{
  size_t  m_szLen{0};
  wchar_t m_pwcDst[123]{};
  ...

public:
  void Bar(double dValue, uint8 u8TotDecimals)
  {
    m_szLen += swprintf_s(&m_pwcDst[m_szLen], _countof(m_pwcDst)-m_szLen, L"%.*f", u8TotDecimals, dValue);
  }
};

How would I convert this to fmtlib WITHOUT copying std::wstring or fmt_memory_buffer? So, I want fmt::format_to to use my existing buffer.

Sander Bouwhuis
  • 647
  • 7
  • 8
  • Can you make a [mcve] where you _do_ the copy that you want to avoid? – Ted Lyngmo Mar 31 '20 at 09:34
  • Something like this: ``` std::wstring wstrTmp = fmt::format(L"{:.{}f}", dValue, u8TotDecimals); wcsncpy(m_pwcDst, _countof(m_pwcDst)-m_u32Len, wstrTmp.c_str(); m_u32Len += wstrTmp.length(); ``` Why doesn't the 'source code' marker not work in this comment?!? It should be outlined like source code. – Sander Bouwhuis Apr 01 '20 at 10:32
  • That's not a [mcve]. From the link: "_Your code examples should be ... Complete – Provide all parts someone else needs to reproduce your problem in the question itself_" – Ted Lyngmo Apr 01 '20 at 10:36
  • This site uses a Question/Answer format, please do not put answers in the question box. You can write answers in the answer box. (I have rolled back) – M.M Apr 02 '20 at 07:20

3 Answers3

0

The closest function there is to swprintf_s() in fmtlib is fmt::format_to_n(). However, it returns the number of characters that would have been written, so you have to take some care using it. Here is how it would look:

void CFoo::Add(double dValue, uint8 u8TotDecimals)
{
    if(pwcSrc) {
        auto space_left = _countof(m_pwcDst) - m_u32Len;
        auto result = fmt::format_to_n(&m_pcwDst, space_left, "{:.{}f}", u8TotDecimals, dValue);
        m_u32Len += std::min(result.size, space_left);
    }
}

However, if you want to do things the proper C++ way, then try to see if you can change the destination buffer to be a std::wstring, and just add directly to that, like so:

void CFoo::Add(double dValue, uint8 u8TotDecimals)
{
    if(pwcSrc)
        m_pwsDst += fmt::format(L"{:.{}f}", u8TotDecimals, dValue);
}
G. Sliepen
  • 7,637
  • 1
  • 15
  • 31
  • Hmmm... I'm a little confused. Your example with {:.*f} crashes with an exception. Only if I remove the asterisk which indicates the length I don't get an exception error. – Sander Bouwhuis Mar 31 '20 at 13:40
  • Ah I forgot to fix the example code, indeed it has to be a `{}` instead of a `*`. – G. Sliepen Apr 02 '20 at 07:03
0

It's not as simple/clear as I would have hoped, so that's a shame. But, this might do what I want:

class CFoo
{
  size_t  m_szLen{0};
  wchar_t m_pwcDst[123]{};
  ...

public:
  void Bar(double dValue, uint8 u8TotDecimals)
  {
    m_pwcDst[m_szLen += std::min(_countof(m_pwcDst)-m_szLen-1, fmt::format_to_n(&m_pwcDst[m_szLen], _countof(m_pwcDst)-m_szLen, FMT_STRING(L"{:.{}f}"), dValue, u8TotDecimals).size)] = L'\0';
  }
};
Sander Bouwhuis
  • 647
  • 7
  • 8
  • By the way, shouldn't it be `fmt::format_to_n(n_pwcDst + m_u32Len, ...)`? And `.size` returns the size that would have been printed without truncation. You can't use it safely to add the terminator. – G. Sliepen Apr 02 '20 at 07:04
  • I changed it to &m_pwcDst[m_u32Len]. Regarding the size... ooooooooh no! I'll have to look into that. – Sander Bouwhuis Apr 02 '20 at 16:28
0

{fmt}'s equivalent of swprintf_s is format_to_n which can be used as follows:

auto result = fmt::format_to_n(buffer, buffer_size, "{:.{}}", value, precision);

In your example value is dValue and precision is u8TotDecimals. result.out will give you the pointer to the end of the output and result.size the total output size if the output wasn't truncated (similarly to snprintf).

vitaut
  • 49,672
  • 25
  • 199
  • 336