Chris@16: // Chris@16: // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See Chris@16: // accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: #ifndef BOOST_LOCALE_FORMAT_HPP_INCLUDED Chris@16: #define BOOST_LOCALE_FORMAT_HPP_INCLUDED Chris@16: Chris@16: #include Chris@16: #ifdef BOOST_MSVC Chris@16: # pragma warning(push) Chris@16: # pragma warning(disable : 4275 4251 4231 4660) Chris@16: #endif Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: Chris@16: Chris@16: namespace boost { Chris@16: namespace locale { Chris@16: Chris@16: /// Chris@16: /// \defgroup format Format Chris@16: /// Chris@16: /// This module provides printf like functionality integrated into iostreams and suitable for localization Chris@16: /// Chris@16: /// @{ Chris@16: /// Chris@16: Chris@16: /// \cond INTERNAL Chris@16: namespace details { Chris@16: Chris@16: template Chris@16: struct formattible { Chris@16: typedef std::basic_ostream stream_type; Chris@16: typedef void (*writer_type)(stream_type &output,void const *ptr); Chris@16: Chris@16: formattible() : Chris@16: pointer_(0), Chris@16: writer_(&formattible::void_write) Chris@16: { Chris@16: } Chris@16: Chris@16: formattible(formattible const &other) : Chris@16: pointer_(other.pointer_), Chris@16: writer_(other.writer_) Chris@16: { Chris@16: } Chris@16: Chris@16: formattible const &operator=(formattible const &other) Chris@16: { Chris@16: if(this != &other) { Chris@16: pointer_=other.pointer_; Chris@16: writer_=other.writer_; Chris@16: } Chris@16: return *this; Chris@16: } Chris@16: Chris@16: template Chris@16: formattible(Type const &value) Chris@16: { Chris@16: pointer_ = static_cast(&value); Chris@16: writer_ = &write; Chris@16: } Chris@16: Chris@16: template Chris@16: formattible const &operator=(Type const &other) Chris@16: { Chris@16: *this = formattible(other); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: friend stream_type &operator<<(stream_type &out,formattible const &fmt) Chris@16: { Chris@16: fmt.writer_(out,fmt.pointer_); Chris@16: return out; Chris@16: } Chris@16: Chris@16: private: Chris@16: static void void_write(stream_type &output,void const * /*ptr*/) Chris@16: { Chris@16: CharType empty_string[1]={0}; Chris@16: output< Chris@16: static void write(stream_type &output,void const *ptr) Chris@16: { Chris@16: output << *static_cast(ptr); Chris@16: } Chris@16: Chris@16: void const *pointer_; Chris@16: writer_type writer_; Chris@16: }; // formattible Chris@16: Chris@16: class BOOST_LOCALE_DECL format_parser { Chris@16: public: Chris@16: format_parser(std::ios_base &ios,void *,void (*imbuer)(void *,std::locale const &)); Chris@16: ~format_parser(); Chris@16: Chris@16: unsigned get_position(); Chris@16: Chris@16: void set_one_flag(std::string const &key,std::string const &value); Chris@16: Chris@16: template Chris@16: void set_flag_with_str(std::string const &key,std::basic_string const &value) Chris@16: { Chris@16: if(key=="ftime" || key=="strftime") { Chris@16: as::strftime(ios_); Chris@16: ios_info::get(ios_).date_time_pattern(value); Chris@16: } Chris@16: } Chris@16: void restore(); Chris@16: private: Chris@16: void imbue(std::locale const &); Chris@16: format_parser(format_parser const &); Chris@16: void operator=(format_parser const &); Chris@16: Chris@16: std::ios_base &ios_; Chris@16: struct data; Chris@16: std::auto_ptr d; Chris@16: }; Chris@16: Chris@16: } Chris@16: Chris@16: /// \endcond Chris@16: Chris@16: /// Chris@16: /// \brief a printf like class that allows type-safe and locale aware message formatting Chris@16: /// Chris@16: /// This class creates a formatted message similar to printf or boost::format and receives Chris@16: /// formatted entries via operator %. Chris@16: /// Chris@16: /// For example Chris@16: /// \code Chris@16: /// cout << format("Hello {1}, you are {2} years old") % name % age << endl; Chris@16: /// \endcode Chris@16: /// Chris@16: /// Formatting is enclosed between curly brackets \c { \c } and defined by a comma separated list of flags in the format key[=value] Chris@16: /// value may also be text included between single quotes \c ' that is used for special purposes where inclusion of non-ASCII Chris@16: /// text is allowed Chris@16: /// Chris@16: /// Including of literal \c { and \c } is possible by specifying double brackets \c {{ and \c }} accordingly. Chris@16: /// Chris@16: /// Chris@16: /// For example: Chris@16: /// Chris@16: /// \code Chris@16: /// cout << format("The height of water at {1,time} is {2,num=fixed,precision=3}") % time % height; Chris@16: /// \endcode Chris@16: /// Chris@16: /// The special key -- a number without a value defines the position of an input parameter. Chris@16: /// List of keys: Chris@16: /// - \c [0-9]+ -- digits, the index of a formatted parameter -- mandatory key. Chris@16: /// - \c num or \c number -- format a number. Optional values are: Chris@16: /// - \c hex -- display hexadecimal number Chris@16: /// - \c oct -- display in octal format Chris@16: /// - \c sci or \c scientific -- display in scientific format Chris@16: /// - \c fix or \c fixed -- display in fixed format Chris@16: /// . Chris@16: /// For example \c number=sci Chris@16: /// - \c cur or \c currency -- format currency. Optional values are: Chris@16: /// Chris@16: /// - \c iso -- display using ISO currency symbol. Chris@16: /// - \c nat or \c national -- display using national currency symbol. Chris@16: /// . Chris@16: /// - \c per or \c percent -- format percent value. Chris@16: /// - \c date, \c time , \c datetime or \c dt -- format date, time or date and time. Optional values are: Chris@16: /// - \c s or \c short -- display in short format Chris@16: /// - \c m or \c medium -- display in medium format. Chris@16: /// - \c l or \c long -- display in long format. Chris@16: /// - \c f or \c full -- display in full format. Chris@16: /// . Chris@16: /// - \c ftime with string (quoted) parameter -- display as with \c strftime see, \c as::ftime manipulator Chris@16: /// - \c spell or \c spellout -- spell the number. Chris@16: /// - \c ord or \c ordinal -- format ordinal number (1st, 2nd... etc) Chris@16: /// - \c left or \c < -- align to left. Chris@16: /// - \c right or \c > -- align to right. Chris@16: /// - \c width or \c w -- set field width (requires parameter). Chris@16: /// - \c precision or \c p -- set precision (requires parameter). Chris@16: /// - \c locale -- with parameter -- switch locale for current operation. This command generates locale Chris@16: /// with formatting facets giving more fine grained control of formatting. For example: Chris@16: /// \code Chris@16: /// cout << format("Today {1,date} ({1,date,locale=he_IL.UTF-8@calendar=hebrew,date} Hebrew Date)") % date; Chris@16: /// \endcode Chris@16: /// - \c timezone or \c tz -- the name of the timezone to display the time in. For example:\n Chris@16: /// \code Chris@16: /// cout << format("Time is: Local {1,time}, ({1,time,tz=EET} Eastern European Time)") % date; Chris@16: /// \endcode Chris@16: /// - \c local - display the time in local time Chris@16: /// - \c gmt - display the time in UTC time scale Chris@16: /// \code Chris@16: /// cout << format("Local time is: {1,time,local}, universal time is {1,time,gmt}") % time; Chris@16: /// \endcode Chris@16: /// Chris@16: /// Chris@16: /// Invalid formatting strings are slightly ignored. This would prevent from translator Chris@16: /// to crash the program in unexpected location. Chris@16: /// Chris@16: template Chris@16: class basic_format { Chris@16: public: Chris@16: typedef CharType char_type; ///< Underlying character type Chris@16: typedef basic_message message_type; ///< The translation message type Chris@16: /// \cond INTERNAL Chris@16: typedef details::formattible formattible_type; Chris@16: /// \endcond Chris@16: Chris@16: typedef std::basic_string string_type; ///< string type for this type of character Chris@16: typedef std::basic_ostream stream_type; ///< output stream type for this type of character Chris@16: Chris@16: Chris@16: /// Chris@16: /// Create a format class for \a format_string Chris@16: /// Chris@16: basic_format(string_type format_string) : Chris@16: format_(format_string), Chris@16: translate_(false), Chris@16: parameters_count_(0) Chris@16: { Chris@16: } Chris@16: /// Chris@16: /// Create a format class using message \a trans. The message if translated first according Chris@16: /// to the rules of target locale and then interpreted as format string Chris@16: /// Chris@16: basic_format(message_type const &trans) : Chris@16: message_(trans), Chris@16: translate_(true), Chris@16: parameters_count_(0) Chris@16: { Chris@16: } Chris@16: Chris@16: /// Chris@16: /// Add new parameter to format list. The object should be a type Chris@16: /// with defined expression out << object where \c out is \c std::basic_ostream. Chris@16: /// Chris@16: template Chris@16: basic_format &operator % (Formattible const &object) Chris@16: { Chris@16: add(formattible_type(object)); Chris@16: return *this; Chris@16: } Chris@16: Chris@16: /// Chris@16: /// Format a string using a locale \a loc Chris@16: /// Chris@16: string_type str(std::locale const &loc = std::locale()) const Chris@16: { Chris@16: std::basic_ostringstream buffer; Chris@16: buffer.imbue(loc); Chris@16: write(buffer); Chris@16: return buffer.str(); Chris@16: } Chris@16: Chris@16: /// Chris@16: /// write a formatted string to output stream \a out using out's locale Chris@16: /// Chris@16: void write(stream_type &out) const Chris@16: { Chris@16: string_type format; Chris@16: if(translate_) Chris@16: format = message_.str(out.getloc(),ios_info::get(out).domain_id()); Chris@16: else Chris@16: format = format_; Chris@16: Chris@16: format_output(out,format); Chris@16: Chris@16: } Chris@16: Chris@16: Chris@16: private: Chris@16: Chris@16: class format_guard { Chris@16: public: Chris@16: format_guard(details::format_parser &fmt) : Chris@16: fmt_(&fmt), Chris@16: restored_(false) Chris@16: { Chris@16: } Chris@16: void restore() Chris@16: { Chris@16: if(restored_) Chris@16: return; Chris@16: fmt_->restore(); Chris@16: restored_ = true; Chris@16: } Chris@16: ~format_guard() Chris@16: { Chris@16: try { Chris@16: restore(); Chris@16: } Chris@16: catch(...) { Chris@16: } Chris@16: } Chris@16: private: Chris@16: details::format_parser *fmt_; Chris@16: bool restored_; Chris@16: }; Chris@16: Chris@16: void format_output(stream_type &out,string_type const &sformat) const Chris@16: { Chris@16: char_type obrk='{'; Chris@16: char_type cbrk='}'; Chris@16: char_type eq='='; Chris@16: char_type comma=','; Chris@16: char_type quote='\''; Chris@16: Chris@16: size_t pos = 0; Chris@16: size_t size=sformat.size(); Chris@16: CharType const *format=sformat.c_str(); Chris@16: while(format[pos]!=0) { Chris@16: if(format[pos] != obrk) { Chris@16: if(format[pos]==cbrk && format[pos+1]==cbrk) { Chris@16: out << cbrk; Chris@16: pos+=2; Chris@16: } Chris@16: else { Chris@16: out<(&out),&basic_format::imbue_locale); Chris@16: Chris@16: format_guard guard(fmt); Chris@16: Chris@16: while(pos < size) { Chris@16: std::string key; Chris@16: std::string svalue; Chris@16: string_type value; Chris@16: bool use_svalue = true; Chris@16: for(;format[pos];pos++) { Chris@16: char_type c=format[pos]; Chris@16: if(c==comma || c==eq || c==cbrk) Chris@16: break; Chris@16: else { Chris@16: key+=static_cast(c); Chris@16: } Chris@16: } Chris@16: Chris@16: if(format[pos]==eq) { Chris@16: pos++; Chris@16: if(format[pos]==quote) { Chris@16: pos++; Chris@16: use_svalue = false; Chris@16: while(format[pos]) { Chris@16: if(format[pos]==quote) { Chris@16: if(format[pos+1]==quote) { Chris@16: value+=quote; Chris@16: pos+=2; Chris@16: } Chris@16: else { Chris@16: pos++; Chris@16: break; Chris@16: } Chris@16: } Chris@16: else { Chris@16: value+=format[pos]; Chris@16: pos++; Chris@16: } Chris@16: } Chris@16: } Chris@16: else { Chris@16: char_type c; Chris@16: while((c=format[pos])!=0 && c!=comma && c!=cbrk) { Chris@16: svalue+=static_cast(c); Chris@16: pos++; Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: if(use_svalue) { Chris@16: fmt.set_one_flag(key,svalue); Chris@16: } Chris@16: else Chris@16: fmt.set_flag_with_str(key,value); Chris@16: Chris@16: if(format[pos]==comma) { Chris@16: pos++; Chris@16: continue; Chris@16: } Chris@16: else if(format[pos]==cbrk) { Chris@16: unsigned position = fmt.get_position(); Chris@16: out << get(position); Chris@16: guard.restore(); Chris@16: pos++; Chris@16: break; Chris@16: } Chris@16: else { Chris@16: guard.restore(); Chris@16: break; Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: Chris@16: // Chris@16: // Non-copyable Chris@16: // Chris@16: basic_format(basic_format const &other); Chris@16: void operator=(basic_format const &other); Chris@16: Chris@16: void add(formattible_type const ¶m) Chris@16: { Chris@16: if(parameters_count_ >= base_params_) Chris@16: ext_params_.push_back(param); Chris@16: else Chris@16: parameters_[parameters_count_] = param; Chris@16: parameters_count_++; Chris@16: } Chris@16: Chris@16: formattible_type get(unsigned id) const Chris@16: { Chris@16: if(id >= parameters_count_) Chris@16: return formattible_type(); Chris@16: else if(id >= base_params_) Chris@16: return ext_params_[id - base_params_]; Chris@16: else Chris@16: return parameters_[id]; Chris@16: } Chris@16: Chris@16: static void imbue_locale(void *ptr,std::locale const &l) Chris@16: { Chris@16: reinterpret_cast(ptr)->imbue(l); Chris@16: } Chris@16: Chris@16: Chris@16: Chris@16: static unsigned const base_params_ = 8; Chris@16: Chris@16: message_type message_; Chris@16: string_type format_; Chris@16: bool translate_; Chris@16: Chris@16: Chris@16: formattible_type parameters_[base_params_]; Chris@16: unsigned parameters_count_; Chris@16: std::vector ext_params_; Chris@16: }; Chris@16: Chris@16: /// Chris@16: /// Write formatted message to stream. Chris@16: /// Chris@16: /// This operator actually causes actual text formatting. It uses the locale of \a out stream Chris@16: /// Chris@16: template Chris@16: std::basic_ostream &operator<<(std::basic_ostream &out,basic_format const &fmt) Chris@16: { Chris@16: fmt.write(out); Chris@16: return out; Chris@16: } Chris@16: Chris@16: Chris@16: /// Chris@16: /// Definition of char based format Chris@16: /// Chris@16: typedef basic_format format; Chris@16: Chris@16: /// Chris@16: /// Definition of wchar_t based format Chris@16: /// Chris@16: typedef basic_format wformat; Chris@16: Chris@16: #ifdef BOOST_HAS_CHAR16_T Chris@16: /// Chris@16: /// Definition of char16_t based format Chris@16: /// Chris@16: typedef basic_format u16format; Chris@16: #endif Chris@16: Chris@16: #ifdef BOOST_HAS_CHAR32_T Chris@16: /// Chris@16: /// Definition of char32_t based format Chris@16: /// Chris@16: typedef basic_format u32format; Chris@16: #endif Chris@16: Chris@16: /// Chris@16: /// @} Chris@16: /// Chris@16: Chris@16: } Chris@16: } Chris@16: Chris@16: #ifdef BOOST_MSVC Chris@16: #pragma warning(pop) Chris@16: #endif Chris@16: Chris@16: #endif Chris@16: Chris@16: /// Chris@16: /// \example hello.cpp Chris@16: /// Chris@16: /// Basic example of using various functions provided by this library Chris@16: /// Chris@16: /// \example whello.cpp Chris@16: /// Chris@16: /// Basic example of using various functions with wide strings provided by this library Chris@16: /// Chris@16: /// Chris@16: Chris@16: // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 Chris@16: