AAO DRAMA/DRAMA2 C++ Interface
DRAMA C++11 and later interface
DRAMA 2 Formatting strings

Introduction

This page describes how to format strings for messages output by DRAMA, to users directly, in error reports and exceptions, and in log messages.

In 2026 (from version 2.5), DRAMA2 depreciated many of its original string output methods (e.g. drama::MessageHandler::MessageUser(), drama::logging::Logger::Log(), DramaTHROW_S(), etc) replacing them by new methods using a more modern and reliable approach to string formatting. This page explains this issue and describes the new approach.

Background

For many of us programming with C++ from its early days, C++ has had issues with string formatting. The original C++ standard approach is to use the I/O stream libraries. A "Hello world" example looks simple:

  std::cout << "Hello World" << std::endl;

Here '<<' is an operator which can be implemented for any argument, so you can implement this for you own classes, creating a type-safe approach to I/O, a big benefit.

But this quickly becomes very long winded. For example, consider this example where I want to output two floating point values with fixed point formatting and a precision of 7.

    double MeanRA = ...;
    double MeanDec = ...;
    ...
    s << std::fixed << std::showpoint << std::setprecision(7);
    std::cout  << '(' << MeanRA << ',' << MeanDec << ')' << std::endl;

And I have also now messed up the default formatting of floating point values in std:cout. To resolve that, I would need to do:

   std::ios::fmtflags initialFlags = std::cout.flags;
   s << std::fixed << std::showpoint << std::setprecision(7);
   std::cout  << '(' << MeanRA << ',' << MeanDec << ')' << std::endl;
   std::cout.flags(initialFlags);

And that is NOT exception safe! (I do have an simpler exception safe approach, but that requires a new class!)

Those of us who started in the C language know it could implement this example with:

    double MeanRA = ...;
    double MeanDec = ...;
    ...
    printf("(%.7f,%.7f)\n", MeanRA, MeanDec);

which is so much simpler! Even without playing with the formatting, complex strings with multiple components are easier to set up and maintain when using C's printf() function.

BUT, the C approach only deals with simple standard types, and can be very unsafe if you pass the wrong type. It won't even handle a std::string variable.

As a result, there have long been searches for a better approach. This finally arrived with the fmt::format library, which was based on the approach used by the python format library. We can be reasonably confident this approach will stick as it has been incorporated into C++20 as the std::format library. Using this library, the above example would become:

   fmt::print("({0:.7f}, {1:.7f})", MeanRA, MeanDec);

Which is a lot better then the streams approach, but you might argue it is still a little more complex then the C printf() approach. The real power comes from its other features:

  1. It is type safe, and (in most cases) checked at compile time.
  2. Formatting of complex types is possible
  3. You can write formatting interfaces for your own types
  4. It is very efficient

Point 1 is very important - most/all problems are picked up at compile type, you can't accidentally pass the wrong argument type.

Point 2 allows things like this example:

  fmt::print("{}", std::vector{1, 2, 3});

which prints the vector in a JSON-like format:

[1, 2, 3]

Point 3 allows you to support your own types, through I admit that some of the support for this is currently very complex. But in many cases, it is quick and easy.

Point 4 is discussed at fmt::format, but the claim is that it has been found to be anywhere from tens of percent to 20-30 times faster than iostreams and sprintf(), especially for numeric formatting.

DRAMA 2 will build with compilers supporting C++11 or later, and given the fmt::format library does build with C++11 onward and has an MIT style license, DRAMA incorporates it as distributed by fmt.dev at particular points in times. The distributed version is upgraded regularly to the latest version found at fmt.dev.

The fmt::format library was incorporated into DRAMA (include file "drama/fmt/format.h" instead of "fmt/format.h") in June 2021 and was then used to create strings supplied to various output routines.

In 2025 (DRAMA Version 2.5) all older approaches to string formatting used by DRAMA 2 were depreciated, with new methods made available using this approach. This works with compilers implementing the C++11 standard and later versions.

fmt::format vs std::format

std::format is not available until you move to C++20, but fmt::format works from C++11 and in many cases is identical. Only at the points where you include the include file, write your own formatters, or call methods like fmt::print, is the use of fmt::format exposed. Given this, we believe it is better to move to using fmt::format with support for older versions of C++ rather then wait for C++20 to become used by all systems using DRAMA. This is important as various systems on which DRAMA 2 is deployed are unlikely to be updated to support C++20 for some time.

Depreciated DRAMA 2 approaches

Prior to this change (and as a result, seen in lots of code written using DRAMA 2), DRAMA 2 code normally used one of these four approaches to formatting strings:

  1. Simple std::string based techniques such as std::to_string combined with the ability to add strings. For example:
        MessageUser(std::string("My result=")+std::to_string(floatVal));
    This gets complex very quickly and doesn't provide any way of changing the details of the formatting (e.g the number of decimal places).
  2. Output via string streams. For example:
        std::ostringstream strStream;
        strStream << "My result=" << floatVal << std::endl;
        MessageUser(strStream.str());
    which is type safe but gives us all of the C++ stream problems we are trying to avoid. It becomes very complex very quickly.
  3. Falling back to C language style formatting. Methods drama::ErsReport(), and drama::logging::Logger::Log() provide examples of this approach.
  4. Using what I called "Safe Printf style formatting". This was introduced a few years after DRAMA 2 was written (replacing, in new code, the older approaches above) and became common. It works reasonable well but is inefficient and requires that each type to be formatted has a stream output operator. The results look like this:
         MessageUser("My result=%",floatVal);
    Here you just use a single percent character to say you have an argument, and the result will look for a stream operator. This approach is type safe but not always at compile time, sometimes the errors only occur at run time, something that is a significant problem for strings in error messages (you can get an exception outputting your error messages). Additionally, there is only limited support for changing the formatting used. Regardless of the flaws, it is a generally easy and reliable approach, so has been used a lot.

DRAMA 2 fmt::format approach

In the new approach, each of the older formatted output methods have been depreciated. They are each replaced by a new method with a particular naming scheme. The new methods take a fmt::format argument string and the relevant arguments, e.g. MessageUser() as above has been replaced by drama::MessageHandler::MessageUserF(), and the above example becomes:

     MessageUserF("My result={0}", floatVal);

This now gives us a compile type safe approach for formatting, with support for complex formatting variations.

Format specifiers.

So just what do the format statements look like?

In most of the cases the syntax is similar to the printf formatting, with the "{}" and ":" used instead of "%". For example, "%03.2f" can be translated to "{:03.2f}".

Here is a basic example

MessageUserF("{0}, {1}, {2}", 'a', 'b', 'c');
// Result: "a, b, c"

The number inside of {} is the index to the argument. If not supplied, then it uses them in order supplied. Putting the number there does allow you to reuse arguments or order them in differently (making it easy to reformat the string). This author prefers to put the numbers in, but many examples do not have them.

Here are some alignment examples

MessageUserF("{:<30}", "left aligned");
// Result: "left aligned                  "
MessageUserF("{:>30}", "right aligned");
// Result: "                 right aligned"
MessageUserF("{:^30}", "centered");
// Result: "           centered           "
MessageUserF("{:*^30}", "centered");  // use '*' as a fill char
// Result: "***********centered***********"

And here we see dynamic precision

MessageUserF("{:.{}f}", 3.14, 1);
// Result: "3.1"

And some floating point format examples

MessageUserF("{:+f}; {:+f}", 3.14, -3.14);  // show it always
// Result: "+3.140000; -3.140000"
MessageUserF("{: f}; {: f}", 3.14, -3.14);  // show a space for positive numbers
// Result: " 3.140000; -3.140000"
MessageUserF("{:-f}; {:-f}", 3.14, -3.14);  // show only the minus -- same as '{:f}; {:f}'
// Result: "3.140000; -3.140000"

See the fmt::format library syntax page for more examples, including how to work with times and ranges. You will need to include drama/fmt/chrono.h to work with times and drama/fmt/ranges.h to work with ranges. (The above examples are based on examples on that page). You will also need drama/fmt/std.h for STL types (e.g. exceptions). A drama::Exception can be also be formatted by including drama/fmt/std.h (since it is a sub-class of std::exception)

DRAMA 2 Types with formatting support

Various DRAMA 2 types have been provided with fmt::format support. The table below lists them (more will be added as found necessary)

Supported type
TypeFormatted as
drama::gitarg::Enum String
drama::gitarg::Real Double
drama::gitarg::Int Long Int
drama::gitarg::String String
drama::gitarg::Bool Returns the string "True" or "False".
drama::gitarg::Filename String
drama::Parameter<> Underlying base type

You can use any format modifiers available to the "formatted as" type.

Formatting your own types

To implement explicit support for your own type, you can see fmt::format library API, but for simple types which basically wrap up simple type (or can be converted to a string), you can provide an implementation of the fmt::format_as() function, e.g. for drama::gitarg::Bool, the following is implemented:

inline auto format_as(gitarg::Bool b) { return bool(b); }

The drama::gitarg::Enum type is a bit more complex. It is a template type declared as:

template <typename LookupClass, typename EnumType> class Enum { ... }

As a result, the format_as specification needs to know the template type details. It so happens that drama::gitarg::Enum provides a std::string method, so we can convert an enum value specification to the corresponding string. This results in the following formatter implementation:

template <typename LookupClass, typename EnumType>
auto format_as(const gitarg::Enum<LookupClass, EnumType> &e) { return std::string(e); }

You can also quickly add support for any class which has an ostream output operator. I used this to support outputting a thread ID value, which does have such an operator but does not have any direct method for converting it to a string:

#include "drama/fmt/ostream.h"
template <> struct fmt::formatter<std::thread::id> : ostream_formatter {};

But please note, this is likely to be less efficient then the previous approaches (as it probably invokes a call that is not needed in the previous examples), so please use the format_as approach if possible.

How to move to the new approach

Checking if this feature is available

If this feature is available, the DRAMA2_FMT macro will be defined. Its value is an integer value of the style "202609L" indicating the version supported. For the initial release, that is that value.

As a result, when implementing code that requires this feature, you can implement a check like this

#ifndef DRAMA2_FMT
#error "DRAMA 2 version not recent enough, you need DRAMA version 2.5 or later to build this"
#endif

Depreciation Warnings

In this section, we look at the Deprecation warnings produced due to these changes, and how to resolve the warnings.

Note
When building with Makefile's generated from dmakefile's, you may find it easier to understand these warnings if you build with make CCDV="". The CCDV make macro refers to a tool which tides up the output but can break up long lines in ways that make dealing with these messages somewhat harder, particularly if you have a wide screen terminal.

Additionally:

Note
There is a way of turning all of these off, see the last part of this section.

MessageUser()

This section looks at a Deprecation warning about the use of drama::MessageHandler::MessageUser. We examine the messages from particular versions two different compilers (CLang and GCC), noting that such messages are likely to change in different versions.

Apple CLang

Here we complied with Apple clang version 16.0.0 (clang-1600.0.26.3)

  examples/dramahello.cpp:59:9: warning: 'MessageUser' is deprecated: Replace MessageUser() by MessageUserF().  See MessageUserF() page for details. [-Wdeprecated-declarations]
     59 |         MessageUser("Hello World - from DRAMA 2, action %", GetActionName());
        |         ^
  ./drama/messagehandler.hh:293:14: note: 'MessageUser' has been explicitly marked deprecated here
    293 |         void MessageUser(const char *format, Types... args) {
        |              ^
  examples/dramahello.cpp:59:9: warning: 'MessageUser<std::string>' is deprecated
  : Replace MessageUser() by MessageUserF().  See MessageUserF() page for details. [-Wdeprecated-declarations]
     59 |         MessageUser("Hello World - from DRAMA 2, action %", GetActionName());
        |         ^
  ./drama/messagehandler.hh:291:9: note: 'MessageUser<std::string>' has been explicitly marked deprecated here
    291 |         D2_FMT_DEPRECATED("Replace MessageUser() by MessageUserF().  See MessageUserF() page for details.") 
        |         ^
  ./drama/util.hh:68:47: note: expanded from macro 'D2_FMT_DEPRECATED'
     68 | #define D2_FMT_DEPRECATED(_a_) __attribute__((deprecated(_a_)))
        |                                               ^
  2 warnings generated.

There are two warnings here, but both of them are referring to dramahello.cpp, line #59. The other messages are the details of the warning, you can see that MessageUser has been marked as depreciated. You can also see the recommendation of "Replace MessageUser() by MessageUserF()".

The reason for two warnings is that MessageUser has an overload, which is part of how the SafePrintf() style formatting (used by MessageUser) is implemented.

GCC

Here is the same warning as reported by gcc version 11.4.0

  examples/dramahello.cpp: In member function 'virtual drama::Request HelloAction::MessageReceived()':
  examples/dramahello.cpp:59:20: warning: 'void drama::MessageHandler::MessageUser(const char*, Types ...) [with Types = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]' is deprecated: Replace MessageUser() by MessageUserF().  See MessageUserF() page for details. [-Wdeprecated-declarations]
     59 |         MessageUser("Hello World - from DRAMA 2, action %", GetActionName());
        |         ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  In file included from ./drama/task.hh:31,
                   from ./drama.hh:406,
                   from examples/dramahello.cpp:37:
  ./drama/messagehandler.hh:293:14: note: declared here
    293 |         void MessageUser(const char *format, Types... args) {
        |              ^~~~~~~~~~~
  ./drama/messagehandler.hh: In instantiation of 'void drama::MessageHandler::MessageUser(const char*, Types ...) [with Types = {std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]':
  examples/dramahello.cpp:59:20:   required from here
  ./drama/messagehandler.hh:301:30: warning: 'void drama::SafePrintf(std::ostream&, const char*, const T&, Types ...) [with T = std::__cxx11::basic_string<char>; Types = {}; std::ostream = std::basic_ostream<char>]' is deprecated: Replace use of SafePrintf  by fmt::format [-Wdeprecated-declarations]
    301 |             drama::SafePrintf(sstrm, format, args...);
        |             ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
  In file included from examples/dramahello.cpp:37:
  ./drama.hh:296:10: note: declared here
    296 |     void SafePrintf( std::ostream &ostream,

All the same information is there but I'm afraid that layout is bit more confused. The important mention of the source of the problem at dramahello.cpp, line #59 is there, as is the recommendation of "Replace MessageUser() by MessageUserF()"

More confusedly, you see mention of drama::SafePrintf. This is because an overload of MessageUser calls that routine and is inline-ed.

Resolving MessageUser

So how do we resolve it. We look at line #59.

MessageUser("Hello World - from DRAMA 2, action %", GetActionName());

We need to rename the method to MessageUserF, and change the format string to the fmt::format style by replacing the "%" by "{0}". The results is

MessageUserF("Hello World - from DRAMA 2, action {0}", GetActionName());

And that compiles without warnings.

Logger::Log()

This section looks at a Deprecation warning about the use of drama::logging::Logger::Log. This method uses printf() style formatting. There is also drama::logging::Logger::SLog which uses the SafePrintf style formatting used by MessageUser above. Both Log and SLog should be replaced by drama::logging::Logger::LogF.

Again examine the messages from particular versions two different compilers (CLang and GCC).

Apple CLang

Here we complied with Apple clang version 16.0.0 (clang-1600.0.26.3)

  examples/logtest.cpp:76:16: warning: 'Log' is deprecated: Replace Log() by LogF() (or LogNF()).  See MessageUserF() page for details on the former. [-Wdeprecated-declarations]
     76 |         logger.Log(D2LOG_INST,  false,
        |                ^
  ./drama/logger.hh:1151:13: note: 'Log' has been explicitly marked deprecated here
   1151 |             D2_FMT_DEPRECATED("Replace Log() by LogF() (or LogNF()). 
   See MessageUserF() page for details on the former.") 
        |             ^
  ./drama/util.hh:68:47: note: expanded from macro 'D2_FMT_DEPRECATED'
     68 | #define D2_FMT_DEPRECATED(_a_) __attribute__((deprecated(_a_)))
        |                                               ^
  1 warning generated.

This has only the one warning, the first message indicating it is in logtest.cpp at line #76. This is all clear - the second line output indicates you need to "Replace Log() by LogF() (or LogNF())".

A warning about drama::logging::Logger::SLog will be more like we saw above with MessageUser, since SLog() is implemented in a similar way with SafePrintf().

GCC

Here is the same warning as reported by gcc version 11.4.0

  examples/logtest.cpp: In member function 'virtual void TestAction::ActionThread(const drama::sds::Id&)':
  examples/logtest.cpp:76:19: warning: 'void drama::logging::Logger::Log(unsigned int, bool, const char*, const char*, ...)' is deprecated: Replace Log() by LogF() (or LogNF()).  See MessageUserF() page for details on the former. [-Wdeprecated-declarations]
     76 |         logger.Log(D2LOG_INST,  false,
        |         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
     77 |                    "LogTst:ActionThread",
        |                    ~~~~~~~~~~~~~~~~~~~~~~
     78 |                    "This = %p:Testing logger Log() method, test string = %s",
        |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     79 |                    (void *)this, "some string #1");
        |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  In file included from ./drama/task.hh:33,
                   from ./drama.hh:406,
                   from examples/logtest.cpp:37:
  ./drama/logger.hh:1153:17: note: declared here
   1153 |            void Log(unsigned level, bool nofmt, const char *prefix,
        |                 ^~~

For this version of GCC, the warning is pretty good, again you can see that the error is in logtest.cpp at line #76, and the solution is part of that same warning.

Resolving Log

Examining line #76, we have

logger.Log(D2LOG_INST,  false,
           "LogTst:ActionThread",
           "This = %p:Testing logger Log() method, test string = %s",
           (void *)this, "some string #1");

Two things to note, first, this is using printf() style formatting.

Second, that second argument (value false) is a flag used to disable formatting (for efficiency). That flag is NOT found in the SLog version nor in LogF. For the most efficient output of a line without any formatting, use drama::logging::Logger::LogNF! But is is not clear that LogNF provides any benefit over LogF given how much compile time optimization is done by the fmt::format library, but since such a method is needed in the implementation, it is made publicly available.

Working through the changes, replace Log by LogF, remove the second argument, and then modify the format string correctly. This gives us:

logger.LogF(D2LOG_INST,  
            "LogTst:ActionThread",
            "This = {0}:Testing logger Log() method, test string = {1}", 
            (void *)this, "some string #1");

DramaTHROW_S()

Here we are looking at a depreciation warning about DramaTHROW_S(). Note that this is a macro, not a method. The macro creates an object of type drama::Exception and then throws that object. This is done via a function named drama::ExceptionThrowSafe, which may appear in the warning messages.

Apple CLang

Here we complied with Apple clang version 16.0.0 (clang-1600.0.26.3)

  ./drama/sds.hh:1296:21: warning: 'ExceptionThrowSafe<unsigned long>' is deprecated: replace DramaTHROW_S() by DramaTHROW_F(). See MessageUserF() page for details. [-Wdeprecated-declarations]
   1296 |                     DramaTHROW_S(status, 
        |                     ^
  ./drama/exception.hh:88:50: note: expanded from macro 'DramaTHROW_S'
     88 | #define DramaTHROW_S(status_,format_,...) drama::ExceptionThrowSafe(__func__, __FILE__,__LINE__,(status_), (format_), __VA_ARGS__)
        |                                                  ^
  ./drama/exception.hh:630:5: note: 'ExceptionThrowSafe<unsigned long>' has been explicitly marked deprecated here
    630 |     D2_FMT_DEPRECATED ("replace DramaTHROW_S() by DramaTHROW_F(). See MessageUserF() page for details.") 
        |     ^
  ./drama/util.hh:68:47: note: expanded from macro 'D2_FMT_DEPRECATED'
     68 | #define D2_FMT_DEPRECATED(_a_) __attribute__((deprecated(_a_)))
        |                                               ^
  1 warning generated.

Regardless of all the lines, note that last one says 1 warning generated. The first line indicates the source of the problem, sds.hh, line #1296.

Note it says to "replace DramaTHROW_S() by DramaTHROW_F().".

GCC

Here is the same error as reported by gcc version 11.4.0

  In file included from drama.hh:245,
                   from util.cpp:34:
  ./drama/sds.hh: In member function 'drama::sds::Id drama::sds::Id::Cell(long unsigned int, bool) const':
  drama/exception.hh:88:68: warning: 'void drama::ExceptionThrowSafe(std::string, const string&, int, StatusType, const char*, Types ...) [with Types = {long unsigned int}; std::string = std::__cxx11::basic_string<char>; StatusType = int]' is deprecated: replace DramaTHROW_S() by DramaTHROW_F(). See MessageUserF() page for details. [-Wdeprecated-declarations]
     88 | #define DramaTHROW_S(status_,format_,...) drama::ExceptionThrowSafe(__func__, __FILE__,__LINE__,(status_), (format_), __VA_ARGS__)
        |                                           ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  ./drama/sds.hh:1296:21: note: in expansion of macro 'DramaTHROW_S'
   1296 |                     DramaTHROW_S(status,
        |                     ^~~~~~~~~~~~
  In file included from drama.hh:245,
                   from util.cpp:34:
  drama/exception.hh:632:10: note: declared here
    632 |     void ExceptionThrowSafe[[ noreturn ]] (const std::string func,
        |          ^~~~~~~~~~~~~~~~~~
  drama/exception.hh: In instantiation of 'void drama::ExceptionThrowSafe(std::string, const string&, int, StatusType, const char*, Types ...) [with Types = {long unsigned int}; std::string = std::__cxx11::basic_string<char>; StatusTy
  pe = int]':
  ./drama/sds.hh:1296:21:   required from here

Again the same information is there but again the layout is bit more confused. The important mention of the source of the problem in sds.hh, line #1296, comes last instead of first. It is also a bit harder to see the text "replace DramaTHROW_S() by DramaTHROW_F()". But it is all there.

Resolving DramaTHROW_S

If have a look at sds.hh, line #1296. It contains:

DramaTHROW_S(status, 
             "Failed to access cell index % of existing SDS array id", index);

So two things to do, replace "_S" by "_F" and replace the "%" by "{0}". This gives us:

DramaTHROW_F(status, 
            "Failed to access cell index {0} of existing SDS array id", index);

This compiles without warning and we are done with that one!

How to disable deprecation warnings

We have no intention of removing the various deprecated methods in the near future, so you may prefer to just turn off the above warnings in your code. This can be done by defining the macro D2_NO_FMT_DEPRECATION_ALERTS before including any DRAMA 2 include files (normally drama.hh)..

You do need to be careful about doing this in library include files meant to be included by other programs, since you are disabling the warnings in all uses of your library, but this does work well for task implementations where there is a task wide include file.

If your intention is to come back an fix them (a good idea), then I suggest you consider this approach. In a task wide include file, add the definition, e.g.

#define D2_NO_FMT_DEPRECATION_ALERTS 

Then in one and only one .cpp file in the task, implementing something like this

#ifdef D2_NO_FMT_DEPRECATION_ALERTS
// Remove definition of D2_NO_FMT_DEPRECATION_ALERTS in include file
//  and then resolve all deprecation warnings about string formatting.
#pragma GCC warning "Please complete move to DRAMA 2.5 DRAMA2 string formatting"
#endif

This will ensure you get a (single) reminder each time you compile.

Deprecated methods

Depreciated methods and their replacements
DepreciatedReplacement
drama::MessageHandler::MessageUser drama::MessageHandler::MessageUserF
drama::ErsReport drama::ErsReportF
drama::Exception::FmtWrite drama::Exception::FmtWriteF
drama::logging::Logger::Log drama::logging::Logger::LogF
drama::logging::Logger::SLog drama::logging::Logger::LogF
More efficient if no arguments to format drama::logging::Logger::LogNF
drama::SafePrintf Use fmt::format and related methods.
drama::TSafePrintf Use fmt::format and related methods. A special version for threads is no longer needed
DramaTHROW_S() DramaTHROW_F()

Semi-automating the changes

When upgrading existing code to move to the new approach, the author's approach involves an abundance of caution, but the approach here does seem to work.

Search forReplace with
MessageUser(MessageUserF(
ErsReportErsReportF
SLogLogF
DramaTHROW_SDramaTHROW_F
%{}

I do this inspecting each change before allowing it to happen, particular for the last items, as the percent character is used in various other cases.

I then search for "<tt>Log(</tt>". This requires extra work - the formatting string is printf() style and must be reworked correctly. Additionally, you may find string arguments using .c_str(), and that can now be removed.

If there any calls to ErsReport() (now ErsReportF()), these must be visited to ensure the status argument is first, it will be last in some cases.

When working through the changes, check for old approaches to creating the format strings (adding std::string items or using std::ostringstream, the later now often being unnecessary.

Also note that in many cases, enum arguments (probably enum class) will need to be cast to integers, or you will need to create a formatter for them.

You can't pass a variable as the format string

For security reasons, compilers will often complain if you try to use the variable in the position of the formatting string. E.g

std::string message;
...
MessageUserF(message);

Instead do:

std::string message;
...
MessageUserF("{}", message);  

Problems with "static const*" class members

A problem has been noted when arguments to a format are a class item declared as "static const" or "static constexpr". This was found to cause a link error. While this occurred with both Apple CLang and GCC, it is unclear to the author what the underlying issue is.

The code triggering the error was:

DramaTHROW_F(GCAM2__INV_EXP_TIME,
            "Exposure specified as {0}, must be non-negative and less then {1}",
             expTime, MAX_EXP_TIME);

where MAX_EXP_TIME was declared in the relevant class as "static constexpr int". The link failed with:

Undefined symbols for architecture arm64:
    "gcam::Task::MAX_EXP_TIME", referenced from:
        gcam::Task::Centroid(drama::thread::TAction*, unsigned int, double, dou
  ble, gcam::Window, std::__1::shared_ptr<gcam::Centroider>, double, bool, bool
  , gcam::DataInfo*) in libgcam2.a[2](gcam2.o)
  ld: symbol(s) not found for architecture arm64
  clang++: error: linker command failed with exit code 1 (use -v to see invocat
  ion)

To resolve this, I put the value into a temporary variable which will probably be optimized out, e.g:

auto maxExpTime = MAX_EXP_TIME; 
DramaTHROW_F(GCAM2__INV_EXP_TIME, 
            "Exposure specified as {0}, must be non-negative and less then {1}",
             expTime, maxExpTime);

fmt::format library copyright notice

As per the License on the fmt::format library, this copyright notice applies to that software, found in the drama_source/Drama2/drama/fmt directory.

Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.