Code View

mstring / mstring-1.3.1.2 / src / MText.cpp
// SPDX-License-Identifier: MIT
#include "MText.hh"

#include <algorithm>
#include <ostream>

#include "MFormat.hh"
#include "MUtil.hh"

TextElementsWriter::TextElementsWriter( std::ostream &stream, std::string &indent )
  : Writer( stream, indent )
{
  // blank
}

void TextElementsWriter::operator( )( const TextElement &text )
{
  if ( text.Macro )
  {
    if ( ! text.Formats.empty( ) )
    {
      // copyfmt saves and restores flags, precision, width, fill and locale:
      // a format never leaks into the text that follows.
      _Stream << _Indent << '{' << std::endl;
      right( );
      _Stream << _Indent << "std::ios originalFormat( nullptr );" << std::endl
              << _Indent << "originalFormat.copyfmt( stream );" << std::endl;
    }

    std::for_each( text.Formats.begin( ), text.Formats.end( ), FormatWriter( _Stream, _Indent ) );
    _Stream << _Indent << "stream << ( " << text.Text << " );" << std::endl;

    if ( ! text.Formats.empty( ) )
    {
      _Stream << _Indent << "stream.copyfmt( originalFormat );" << std::endl;
      left( );
      _Stream << _Indent << '}' << std::endl;
    }
  }
  else
  {
    _Stream << _Indent << "stream << ";
    WriteStringLiteral( _Stream, text.Text, _Indent, 10 );
    _Stream << ';' << std::endl;
  }
}

const char *const AppendHelperName = "InternalAppend";

namespace
{
  void AppendElement( std::ostream &stream, std::string &indent, const TextElement &text )
  {
    if ( ! text.Macro )
    {
      stream << indent << "internal_out += ";
      WriteStringLiteral( stream, text.Text, indent, 16 );
      stream << ';' << std::endl;
    }
    else if ( text.Formats.empty( ) )
    {
      stream << indent << Mode.Helper( AppendHelperName ) << "( internal_out, loc, ( " << text.Text << " ) );" << std::endl;
    }
    else
    {
      // Formats need a stream; a fresh one per value, so nothing leaks.
      stream << indent << '{' << std::endl;
      indent.append( "  " );
      stream << indent << "std::ostringstream stream;" << std::endl
             << indent << "stream.imbue( loc );" << std::endl;
      std::for_each( text.Formats.begin( ), text.Formats.end( ), FormatWriter( stream, indent ) );
      stream << indent << "stream << ( " << text.Text << " );" << std::endl
             << indent << "internal_out += stream.str( );" << std::endl;
      indent.erase( 0, 2 );
      stream << indent << '}' << std::endl;
    }
  }
}

void WriteLocaleAppend( std::ostream &stream, std::string &indent, const LocaleMessage &locale )
{
  stream << indent << "// ENCODE : " << locale.Encode << std::endl;

  for ( const TextElement &text : locale.TextElements )
  {
    AppendElement( stream, indent, text );
  }
}

void WriteAppendHelper( std::ostream &stream )
{
  Mode.OpenHelpers( stream, "APPEND" );
  stream << "  template <typename T>" << std::endl
         << "  inline constexpr bool InternalIsCharacter =" << std::endl
         << "    std::is_same_v<T, char> || std::is_same_v<T, signed char> || std::is_same_v<T, unsigned char> ||" << std::endl
         << "    std::is_same_v<T, wchar_t> || std::is_same_v<T, char16_t> || std::is_same_v<T, char32_t>;" << std::endl
         << std::endl
         << "  // Append a value exactly as `stream << value` on a stream imbued with" << std::endl
         << "  // loc would write it, without a stream where that is possible." << std::endl
         << "  template <typename T>" << std::endl
         << "  inline void " << AppendHelperName << "( std::string &out, std::locale const &loc, T const &value )" << std::endl
         << "  {" << std::endl
         << "    if constexpr ( std::is_convertible_v<T const &, std::string_view> )" << std::endl
         << "    {" << std::endl
         << "      out += std::string_view( value );" << std::endl
         << "      return;" << std::endl
         << "    }" << std::endl
         << "    else if constexpr ( std::is_integral_v<T> && ! std::is_same_v<T, bool> && ! InternalIsCharacter<T> )" << std::endl
         << "    {" << std::endl
         << "      if ( loc == std::locale::classic( ) )" << std::endl
         << "      {" << std::endl
         << "        char buffer[24];" << std::endl
         << "        std::to_chars_result const written = std::to_chars( buffer, buffer + sizeof buffer, value );" << std::endl
         << "        out.append( buffer, written.ptr );" << std::endl
         << "        return;" << std::endl
         << "      }" << std::endl
         << "    }" << std::endl
         << std::endl
         << "    std::ostringstream stream;" << std::endl
         << "    stream.imbue( loc );" << std::endl
         << "    stream << value;" << std::endl
         << "    out += stream.str( );" << std::endl
         << "  }" << std::endl;
  Mode.CloseHelpers( stream );
}

void WriteLocaleText( std::ostream &stream, std::string &indent, const LocaleMessage &locale )
{
  stream << indent << "// ENCODE : " << locale.Encode << std::endl;
  std::for_each( locale.TextElements.begin( ), locale.TextElements.end( ),
                 TextElementsWriter( stream, indent ) );
}