1.2. Argument Formatting

A frequent programming task is generating information for the user. Often you have to assemble a string from several parts. Most of the time there is some sort of format string to determine the basic information with some placeholders at the desired position which are substituted with actual values.

A lot of people are using the printf() family for outputting. Unfortunately these are not typesafe. And nor are they appropriate for translations when the positions of the substituted parameters are swapped for grammatical reasons.

So I decided to implement a typesafe approach which also takes the position of the substituted values into account: each inserted value is converted into its string representation and substitutes the placeholder with the lowest number. Since such a placeholder consists of a percent sign and a single digit there are up to ten substitutions possible.

The built-in methods handle the common data types like int, float or std::string. But it is also easily possible to extend the output capabilities for your own data types. No changes inside the log4sendpp framework are needed, it needn't even be in the log4sendpp namespace.

In the case you don't really want to output the content of an object because it is too complex you may use a default template instead which outputs only the class name. To avoid pontential conflicts these templates must be enabled by setting the macro LOG4SENDPP_ENABLE_DEFAULT_FORMATTER before including the first log4sendpp header file.

It is also possible to reuse already existing streaming operators for std::basic_ostream. The macros LOG4SENDPP_OSTREAMABLE and LOG4SENDPP_OWSTREAMABLE create code for log4sendpp which uses the standard streaming functions.

This collection of functions is inspired by the QString class which is part of the Qt™ framework by Trolltech.


  class Person 1
  {
    public:

      Person(std::string in_name, unsigned in_age)
        : name(in_name)
        , age(n_age)
      {}

      String toString() const
      {
        String ret;
        ret << name << "(" << age << ")";
        return ret;
      }

    private:

      std::string  name;
      unsigned     age;
  };

  String & operator<< (String &formatter, const Person &pers)
  {
     formatter << pers.toString(); 2
     return formatter;
  }

  class AnotherPerson : public Person
  {
  // ..
  };

  std::ostream & operator<< (std::ostream &os, const AnotherPerson &pers)
  {
     os << pers.toString();
     return os;
  }

  LOG4SENDPP_OSTREAMABLE(AnotherPerson) 3

  Person pers("Bob", 6);
  AnotherPerson otherpers("Alice", 7);
  String  format = "%5 and %4 say: %3 plus %2 gives %1";
  format << "three" << "two" << "one" << pers << otherpers;
  std::cout << format << std::endl;

1

Create a user defined data type with some internal data elements. Implement a method to create a string representation of the internal variables.

2

Output the string representation of the object.

The resulting output: Alice(7) and Bob(6) say: one plus two gives three

3

Output the string using an existing standard streaming operator.