This class can be used to offer customized format strings to end users. For example, when it should be allowed to end-users to store output format strings of complex type.
For this, the format string definition of ALib {lang::format;Formatter} types is extended to support identifiers, which act as placeholders for object values. The identifiers placed in the string have to match to the normal formatting placeholders in respect to their total number and value type.
In the constructor of this class, a custom format string which whose syntax is based on standard ALib format strings is passed. However, before using the format string, it is processed as follows:
It could be said, that the format string given "is compiled" with the constructor of this class.
The only interface method Format then accepts a boxed object of custom type that is used to collect the format string's placeholder data, by using the stored callback functions.
The identifiers are denoted by prefixing them with (customizable) character '@'
.
A vector of objects of inner type IdentifierEntry needs to be defined and passed to the constructor of this class. Field IdentifierEntry::Name contains the 'identifier' string which is removed from the format string if found. Field IdentifierEntry::Callback has to point to a callback function used to retrieve the data when the identifier is found in the format string.
The whole approach is useful in cases where an application wants to allow a customizable output format of data objects.
Let us assume, an application uses an enumeration and a simple struct:
In the application's configuration file, a custom output format for objects of type Person should be user-defined. We document to the user that she can use Python-style or Java-style output formats - extended by the ability to place the following three identifiers in the string:
A possible format string then would be:
"{@name} is aged {@age} and his/her hobby is {@hobby}"
To tell this class how to retrieve the replacement values, we need to define three callback functions:
It is a good idea to place the callback functions in an anonymous (sub-) namespace as they are only referred to once (in the next step).
As this sample is very trivial, the second parameter AString&
is not used and named. Details on the purpose and use of this parameter is addressed below.
As a next step, these functions need to be collected together in a "translation table". The table primarily holds a string denoting the replacement identifier and a pointer to the corresponding callback function. For convenience, the table type is provided with using
definition TCallbackTable.
In our sample, the definition of the table looks like this:
This is all we need! Of course, an external declaration of our table PersonCallbacks should be placed somewhere in the project's header file. With this, a code like this may now use the custom formatter strings:
Running the code above produces the following output:
Sue is aged 28 and his/her hobby is hacking John is aged 35 and his/her hobby is hacking
Serializing objects in a custom format (e.g., a user could define its own JSon object output) should be the most obvious and frequent use case. And this is what the classes name suggests. Nevertheless, the callback methods might be more complex than just returning "properties" of objects. The next sections gives more details on the custom callbacks.
The call back function's signature is defined with using
-statement TCallback which evaluates to
Box (*)(const Box&, AString&)
The return type is Box, which allows the callback function to return objects of an arbitrary type, respectively all types that are equipped to be used with ALib formatters.
The first input parameter provides the data object passed to method Format. If the callback functions are dedicated to a property formatter that receives a certain object type (what they usually are), the custom type can be straightly unboxed.
The second parameter is an AString object which optionally can be used to assemble string objects in more complex callback methods. Note, that if this string is not empty after the callback invocation, the result is copied to a string buffer allocated in the heap memory. In this case, the box object returned by the callback is ignored and instead the copy of the string is passed to the formatter. If C++ string constants (e.g "true"
are to be returned by the callbacks, those constants do not need to be copied to the buffer, as their memory is statically allocated. In other words, the AString buffer provided in the second parameter is needed to be used only in cases that a string gets assembled in the callback function!
The escape character used to find custom identifiers in the format string is defined with parameter ESCCharacter of the constructor and defaults to '@'. This escape character is searched in the format string. If found, the identifier is read by consuming alphabetic characters. Optionally, the end of a custom identifier can be marked with an additional escape character. Hence, the two format strings
"{@name}" "{@name@}"
are both valid and equal.
In case of FormatterPythonStyle format strings, it is a good idea to place the identifier right inside the brackets. It just looks very intuitive. However, these versions:
"@name{}" "{}@name"
are also valid custom format strings.
In case of FormatterJavaStyle, which uses '%' as its escape character, we consider the best option to put the custom identifier in front of each '%' symbol. The string of the example given above would then look like this:
"@name%s is aged @age%d and his/her hobby is @hobby%s"
Doing it this way, the '%' symbol acts as a very natural delimiter for the custom identifier.
Furthermore, field IdentifierEntry::MinimumRecognitionLength allows abbreviations of identifier names. It denotes the minimum number of characters to be* matched. As in the above sample a value of 1
is provided, each identifier of the custom format string can be abbreviated down to one character. Consequently the following format string samples are all equal and allowed:
"{@name}" "{@nam}" "{@na}" "{@n}"
Finally, to insert the escape character itself into the format string, it has to be doubly inserted ("@@"
).
If ALib Expressions is compiled with the library, a utility class similar to this one is available with ExpressionFormatter. With this, complex expressions and calculations might be used instead of only having simple identifiers as replacements.
Definition at line 195 of file propertyformatter.hpp.
#include <propertyformatter.hpp>
Inner Type Index: | |
struct | IdentifierEntry |
The entry type of translation table. More... | |
Public Type Index: | |
using | TCallback = Box (*)(const Box&, AString&) |
using | TCallbackTable = std::vector<IdentifierEntry> |
Type definition of the callback table. | |
Public Method Index: | |
ALIB_API | PropertyFormatter (const String customFormatString, const TCallbackTable &propertyTable, SPFormatter formatter=nullptr, character ESCCharacter='@') |
ALIB_API void | Format (AString &target, const Box &src) |
Protected Type Index: | |
using | TCallbackResultTable = std::vector<const IdentifierEntry*> |
Protected Field Index: | |
TCallbackResultTable | callBacks |
The callback functions to receive the format data. | |
AString | formatString |
The resulting format string passed to ALib formatters. | |
AString | propertyFormatString |
The original format string. Used only for exception information. | |
SPFormatter | stdFormatter |
The formatter (as given in the constructor). | |
The signature of the callback functions. See Callback Functions for more information.
Definition at line 200 of file propertyformatter.hpp.
|
protected |
Internal type definition for the list of callback table entries collected in the constructor by parsing the extended format string.
Definition at line 224 of file propertyformatter.hpp.
using TCallbackTable = std::vector<IdentifierEntry> |
Type definition of the callback table.
Definition at line 218 of file propertyformatter.hpp.
|
protected |
The callback functions to receive the format data.
Definition at line 236 of file propertyformatter.hpp.
|
protected |
The resulting format string passed to ALib formatters.
Definition at line 233 of file propertyformatter.hpp.
|
protected |
The original format string. Used only for exception information.
Definition at line 230 of file propertyformatter.hpp.
|
protected |
The formatter (as given in the constructor).
Definition at line 227 of file propertyformatter.hpp.
PropertyFormatter | ( | const String | customFormatString, |
const TCallbackTable & | propertyTable, | ||
SPFormatter | formatter = nullptr, | ||
character | ESCCharacter = '@' ) |
Constructor. Processes the given format string and builds internal structures which are then used with invocations of method Format.
customFormatString | The format string as described in the class documentation. |
propertyTable | Table with property identifier names and callback functions to retrieve the property values. |
formatter | The formatter to use. Defaults to nullptr which selects the default formatter. |
ESCCharacter | The prefix used to search identifiers in customFormatString. Defaults to '@' . |
Definition at line 16 of file propertyformatter.cpp.
Writes the formatted output of the properties of the given TFormattable object to a given target string.
target | The target string to write into. |
src | The custom object which is passed to the callback methods to collect the formatter arguments. |
Definition at line 92 of file propertyformatter.cpp.