diff --git a/Doxyfile b/Doxyfile index c1aa43e8..5f4c4e2d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1588,7 +1588,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = BUILDING_DOXYGEN # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/Sources/Plasma/CoreLib/plFormat.cpp b/Sources/Plasma/CoreLib/plFormat.cpp index 6e7d7a78..754dd6a6 100644 --- a/Sources/Plasma/CoreLib/plFormat.cpp +++ b/Sources/Plasma/CoreLib/plFormat.cpp @@ -50,7 +50,7 @@ Mead, WA 99021 namespace plFormat_Private { - static const char *_scanNextFormat(IFormatDataObject &data) + static const char *_scanNextFormat(_IFormatDataObject &data) { hsAssert(data.fFormatStr, "Passed a null format string!"); @@ -64,7 +64,7 @@ namespace plFormat_Private return ptr; } - static void _fetchPrefixChunk(IFormatDataObject &data) + static void _fetchPrefixChunk(_IFormatDataObject &data) { do { const char *next = _scanNextFormat(data); @@ -81,7 +81,7 @@ namespace plFormat_Private } while (0); } - FormatSpec FetchNextFormat(IFormatDataObject &data) + FormatSpec _FetchNextFormat(_IFormatDataObject &data) { _fetchPrefixChunk(data); hsAssert(*data.fFormatStr == '{', "Too many actual parameters for format string"); @@ -135,13 +135,13 @@ namespace plFormat_Private spec.fDigitClass = kDigitChar; break; case 'f': - spec.fFloatClass = kFloatF; - break; - case 'g': - spec.fFloatClass = kFloatG; + spec.fFloatClass = kFloatFixed; break; case 'e': - spec.fFloatClass = kFloatE; + spec.fFloatClass = kFloatExp; + break; + case 'E': + spec.fFloatClass = kFloatExpUpper; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -166,7 +166,7 @@ namespace plFormat_Private } } - plString _IFormat(plFormat_Private::IFormatDataObject &data) + plString _IFormat(plFormat_Private::_IFormatDataObject &data) { _fetchPrefixChunk(data); hsAssert(*data.fFormatStr == 0, "Not enough actual parameters for format string"); diff --git a/Sources/Plasma/CoreLib/plFormat.h b/Sources/Plasma/CoreLib/plFormat.h index c6a9be4c..432ff7b7 100644 --- a/Sources/Plasma/CoreLib/plFormat.h +++ b/Sources/Plasma/CoreLib/plFormat.h @@ -47,85 +47,105 @@ Mead, WA 99021 #include #include -/* (TODO: Make this table doxygen-friendly) +#ifdef BUILDING_DOXYGEN // Doxygen doesn't appear to support variadic templates yet +/** Format a string using type-safe arguments + * \param format The format string -- see below for details * - * FORMAT SPECIFICATION + * Character Sequence | Description + * ------------------ | ----------- + * `{}` | Format a value (using defaults) + * `{{` | Escape for a single '{' char + * `{options}` | Format a value, with the specified options (see below) * - * {} - format a value (defaults) - * {{ - Escape for a single '{' char - * {options} - Format a value, with the specified options (see below) + * Formatting Options + * ------------------ * - * Options: - * < - Align left - * > - Align right - * NNN - Pad to NNN characters (minimum - can be more) - * + - Show a '+' char for positive signed values (decimal only) - * _C - Use C as the pad character (only '\001'..'\177' supported for now) - * x - Hex (lower-case) - * X - Hex (upper-case) - * o - Octal - * b - Binary - * d - Decimal (default) -- when used with char types, outputs a - * number instead of the UTF representation of the char - * c - UTF character (default for character types) - * FFF.EEE - Use FFF.EEE floating point precision - * f - Use 'f' format for floating point numbers - * g - Use 'g' format for floating point numbers - * e - Use 'e' format for floating point numbers + * Format Option | Description + * ------------- | ----------- + * `<` | Align left + * `>` | Align right + * `NNN` | Pad to NNN characters (minimum - can be more) + * `+` | Show a '+' char for positive signed values (decimal only) + * `_C` | Use C as the pad character (only '\001'..'\177' supported for now) + * `x` | Hex (lower-case) + * `X` | Hex (upper-case) + * `o` | Octal + * `b` | Binary + * `d` | Decimal (default) -- when used with char types, outputs a number instead of the UTF representation of the char + * `c` | UTF character (default for character types) + * `FFF.EEE` | Use FFF.EEE floating point precision + * `f` | Fixed floating point format (ddd.ddd) + * `e` | Exponent notation for floating point (d.ddde[+/-]dd) + * `E` | Same as 'e' format, but with upper case E (d.dddE[+/-]dd) */ +plString plFormat(const char *format, ...); +#endif + -// For internal use by plFormat and its helper function namespace plFormat_Private { enum Alignment : unsigned char { - kAlignDefault, kAlignLeft, kAlignRight + kAlignDefault, /**< Left for strings, right for numbers */ + kAlignLeft, /**< Left alignment */ + kAlignRight /**< Right alignment */ }; enum DigitClass : unsigned char { - kDigitDefault, kDigitDec, kDigitDecAlwaysSigned, - kDigitHex, kDigitHexUpper, kDigitOct, kDigitBin, kDigitChar + kDigitDefault, /**< Default digit formatting */ + kDigitDec, /**< Format as decimal integer */ + kDigitDecAlwaysSigned, /**< Same as `kDigitDec`, but include a '+' for positive numbers too */ + kDigitHex, /**< Hex integer (assume unsigned) */ + kDigitHexUpper, /**< Hex integer with upper-case digits */ + kDigitOct, /**< Octal integer (assume unsigned) */ + kDigitBin, /**< Binary integer (assume unsigned) */ + kDigitChar /**< Single unicode character (as UTF-8) */ }; enum FloatClass : unsigned char { - kFloatDefault, kFloatE, kFloatF, kFloatG + kFloatDefault, /**< Use Fixed or Exp format depending on value */ + kFloatFixed, /**< Use Fixed notation (ddd.ddd) */ + kFloatExp, /**< Use Exp notation (d.ddde[+/-]dd) */ + kFloatExpUpper /**< Same as `kFloatExp`, but with an upper-case E */ }; + /** Represents a parsed format tag, for use in formatter implementations. */ struct FormatSpec { - int fPrecisionLeft = 0; // Also used for padding - int fPrecisionRight = 0; + int fPrecisionLeft = 0; /**< Requested padding and/or precision */ + int fPrecisionRight = 0; /**< Requested precision after the . for floating-point */ - char fPadChar = 0; - Alignment fAlignment = kAlignDefault; - DigitClass fDigitClass = kDigitDefault; - FloatClass fFloatClass = kFloatDefault; + char fPadChar = 0; /**< Explicit padding char (default is space) */ + Alignment fAlignment = kAlignDefault; /**< Requested pad alignment */ + DigitClass fDigitClass = kDigitDefault; /**< Requested int formatting */ + FloatClass fFloatClass = kFloatDefault; /**< Requested float formatting */ }; - struct IFormatDataObject + // These need to be publically visible for the macros below, but shouldn't + // be used directly outside of plFormat and its macros + struct _IFormatDataObject { const char *fFormatStr; std::list> fOutput; }; - extern FormatSpec FetchNextFormat(IFormatDataObject &data); + extern FormatSpec _FetchNextFormat(_IFormatDataObject &data); } -// Fun fact: You can add your own formatters by declaring -// PL_FORMAT_TYPE(mytype) in a header, and -// PL_FORMAT_IMPL(mytype) { ... } in a source file - +/** Declare a formattable type for `plFormat`. + * \sa PL_FORMAT_IMPL() + */ #define PL_FORMAT_TYPE(_type) \ extern plStringBuffer _impl_plFormat_DataHandler( \ const plFormat_Private::FormatSpec &format, _type value); \ namespace plFormat_Private \ { \ template \ - plString _IFormat(IFormatDataObject &data, _type value, _Args... args) \ + plString _IFormat(_IFormatDataObject &data, _type value, _Args... args) \ { \ - plFormat_Private::FormatSpec format = plFormat_Private::FetchNextFormat(data); \ + plFormat_Private::FormatSpec format = plFormat_Private::_FetchNextFormat(data); \ data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \ return _IFormat(data, args...); \ } \ @@ -133,17 +153,41 @@ namespace plFormat_Private template \ plString plFormat(const char *fmt_str, _type value, _Args... args) \ { \ - plFormat_Private::IFormatDataObject data; \ + plFormat_Private::_IFormatDataObject data; \ data.fFormatStr = fmt_str; \ - plFormat_Private::FormatSpec format = plFormat_Private::FetchNextFormat(data); \ + plFormat_Private::FormatSpec format = plFormat_Private::_FetchNextFormat(data); \ data.fOutput.push_back(_impl_plFormat_DataHandler(format, value)); \ return plFormat_Private::_IFormat(data, args...); \ } +/** Provide the implementation for a formattable type for `plFormat`. + * \sa PL_FORMAT_TYPE(), PL_FORMAT_FORWARD() + * + * Example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * PL_FORMAT_IMPL(const MyType &) + * { + * return plFormat("MyType[data={},count={}]", value.data, value.count); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ #define PL_FORMAT_IMPL(_type) \ plStringBuffer _impl_plFormat_DataHandler( \ const plFormat_Private::FormatSpec &format, _type value) +/** Shortcut to call another `PL_FORMAT_IMPL` formatter. + * \sa PL_FORMAT_IMPL() + * + * Example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * PL_FORMAT_IMPL(const MyType &) + * { + * return PL_FORMAT_FORWARD(format, value.ToString()); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ #define PL_FORMAT_FORWARD(format, fwd_value) \ _impl_plFormat_DataHandler((format), (fwd_value)) @@ -174,10 +218,10 @@ PL_FORMAT_TYPE(const std::wstring &) // To use other formats, don't pass us a bool directly... PL_FORMAT_TYPE(bool) -// End of the chain -- emits the last piece (if any) and builds the final string namespace plFormat_Private { - plString _IFormat(IFormatDataObject &data); + // End of the chain -- emits the last piece (if any) and builds the final string + plString _IFormat(_IFormatDataObject &data); } #endif // plFormat_Defined