Add attributes for gcc (and compatible) and msvc.
And fix remaining issues.
Related #2570
Change-Id: I9b46559c2554404b309aa8710eb34ffa128d8cae
#include "bias.h"
#include <cassert>
+#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstdlib>
int64_t numUpdatesExpected = state.histogramSize().numUpdates();
if (numUpdatesFromSamples != numUpdatesExpected)
{
- std::string mesg = gmx::formatString("The number of AWH updates in the checkpoint file (%ld) does not match the total number of AWH samples divided by the number of samples per update for %d sharing AWH bias(es) (%ld/%d=%ld)",
+ std::string mesg = gmx::formatString("The number of AWH updates in the checkpoint file (%" PRId64 ") does not match the total number of AWH samples divided by the number of samples per update for %d sharing AWH bias(es) (%" PRId64 "/%d=%" PRId64 ")",
numUpdatesExpected,
params.numSharedUpdate,
numSamples,
*/
if (numUpdatesFromSamples % state.histogramSize().numUpdates() == 0)
{
- mesg += gmx::formatString(" Or the run you continued from used %ld sharing simulations, whereas you now specified %d sharing simulations.",
+ mesg += gmx::formatString(" Or the run you continued from used %" PRId64 " sharing simulations, whereas you now specified %d sharing simulations.",
numUpdatesFromSamples/state.histogramSize().numUpdates(),
params.numSharedUpdate);
}
#include "config.h"
#include <cassert>
+#include <cinttypes>
#include <climits>
#include <cmath>
#include <cstdio>
case DlbState::onUser:
return forceDlbOffOrBail(dlbState, reasonStr + " In load balanced runs binary reproducibility cannot be ensured.", mdlog);
default:
- gmx_fatal(FARGS, "Death horror: undefined case (%d) for load balancing choice", dlbState);
+ gmx_fatal(FARGS, "Death horror: undefined case (%d) for load balancing choice", static_cast<int>(dlbState));
}
}
{
if (state_local->ddp_count > dd->ddp_count)
{
- gmx_fatal(FARGS, "Internal inconsistency state_local->ddp_count (%d) > dd->ddp_count (%ld)", state_local->ddp_count, dd->ddp_count);
+ gmx_fatal(FARGS, "Internal inconsistency state_local->ddp_count (%d) > dd->ddp_count (%" PRId64 ")", state_local->ddp_count, dd->ddp_count);
}
if (state_local->ddp_count_cg_gl != state_local->ddp_count)
#include "gmxpre.h"
#include <cassert>
+#include <cinttypes>
#include <cmath>
#include <cstdio>
/* Check if the largest divisor is more than nnodes^2/3 */
if (ldiv*ldiv*ldiv > nnodes_div*nnodes_div)
{
- gmx_fatal(FARGS, "The number of ranks you selected (%ld) contains a large prime factor %ld. In most cases this will lead to bad performance. Choose a number with smaller prime factors or set the decomposition (option -dd) manually.",
+ gmx_fatal(FARGS, "The number of ranks you selected (%" PRId64 ") contains a large prime factor %" PRId64 ". In most cases this will lead to bad performance. Choose a number with smaller prime factors or set the decomposition (option -dd) manually.",
nnodes_div, ldiv);
}
}
if (static_cast<int>(ed.edpar.size()) != EDstate->nED)
{
gmx_fatal(FARGS, "The number of essential dynamics / flooding groups is not consistent.\n"
- "There are %d ED groups in the .cpt file, but %d in the .edi file!\n"
+ "There are %d ED groups in the .cpt file, but %zu in the .edi file!\n"
"Are you sure this is the correct .edi file?\n", EDstate->nED, ed.edpar.size());
}
}
const char *dimString[] = { "X", "Y", "Z" };
/* Spline values */
- SCOPED_TRACE(formatString("Testing spline values with tolerance of %ld", ulpToleranceSplineValues));
+ SCOPED_TRACE(formatString("Testing spline values with tolerance of %d", ulpToleranceSplineValues));
TestReferenceChecker splineValuesChecker(rootChecker.checkCompound("Splines", "Values"));
splineValuesChecker.setDefaultTolerance(relativeToleranceAsUlp(1.0, ulpToleranceSplineValues));
for (int i = 0; i < DIM; i++)
/* Spline derivatives */
const auto ulpToleranceSplineDerivatives = 4 * ulpToleranceSplineValues;
/* 4 is just a wild guess since the derivatives are deltas of neighbor spline values which could differ greatly */
- SCOPED_TRACE(formatString("Testing spline derivatives with tolerance of %ld", ulpToleranceSplineDerivatives));
+ SCOPED_TRACE(formatString("Testing spline derivatives with tolerance of %d", ulpToleranceSplineDerivatives));
TestReferenceChecker splineDerivativesChecker(rootChecker.checkCompound("Splines", "Derivatives"));
splineDerivativesChecker.setDefaultTolerance(relativeToleranceAsUlp(1.0, ulpToleranceSplineDerivatives));
for (int i = 0; i < DIM; i++)
TestReferenceChecker gridValuesChecker(rootChecker.checkCompound("NonZeroGridValues", "RealSpaceGrid"));
const auto ulpToleranceGrid = 2 * ulpToleranceSplineValues * static_cast<int>(ceil(sqrt(atomCount)));
/* 2 is empiric; sqrt(atomCount) assumes all the input charges may spread onto the same cell */
- SCOPED_TRACE(formatString("Testing grid values with tolerance of %ld", ulpToleranceGrid));
+ SCOPED_TRACE(formatString("Testing grid values with tolerance of %d", ulpToleranceGrid));
if (!gridValuesSizeAssigned)
{
previousGridValuesSize = nonZeroGridValues.size();
if ((precision != sizeof(float)) && !bDouble)
{
gmx_fatal(FARGS, "Unknown precision in file %s: real is %d bytes "
- "instead of %lu or %lu",
+ "instead of %zu or %zu",
gmx_fio_getname(fio), precision, sizeof(float), sizeof(double));
}
gmx_fio_setprecision(fio, bDouble);
gmx::ArrayRef<const std::string> intfn = opt2fns("-o", NFILE, fnm);
if (intfn.size() != 2)
{
- gmx_fatal(FARGS, "No or not correct number (2) of output-files: %ld", intfn.size());
+ gmx_fatal(FARGS, "No or not correct number (2) of output-files: %td", intfn.size());
}
calc_tetra_order_interface(ndxfnm, tpsfnm, trxfnm, binwidth, nsttblock, &frames, &xslices, &yslices, sg1, sg2, &intfpos, oenv);
writesurftoxpms(intfpos, frames, xslices, yslices, binwidth, intfn, nlevels);
gmx::ArrayRef<const std::string> spectra = opt2fns("-Spect", NFILE, fnm);
if (spectra.size() != 2)
{
- gmx_fatal(FARGS, "No or not correct number (2) of output-files: %ld", spectra.size());
+ gmx_fatal(FARGS, "No or not correct number (2) of output-files: %td", spectra.size());
}
powerspectavg(intfpos, frames, xslices, yslices, spectra);
}
gmx::ArrayRef<const std::string> raw = opt2fns("-or", NFILE, fnm);
if (raw.size() != 2)
{
- gmx_fatal(FARGS, "No or not correct number (2) of output-files: %ld", raw.size());
+ gmx_fatal(FARGS, "No or not correct number (2) of output-files: %td", raw.size());
}
writeraw(intfpos, frames, xslices, yslices, raw);
}
/* format error occured */
case sError:
- gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %ld with char %c", listname, pos-startpos, *(pos-1));
+ gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %td with char %c", listname, pos-startpos, *(pos-1));
/* logical error occured */
case sZero:
- gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %ld: eigenvector 0 is not valid", listname, pos-startpos);
+ gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %td: eigenvector 0 is not valid", listname, pos-startpos);
case sSmaller:
- gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %ld: second index %d is not bigger than %d", listname, pos-startpos, end_number, number);
+ gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %td: second index %d is not bigger than %d", listname, pos-startpos, end_number, number);
}
++pos; /* read next character */
} /*scanner has finished */
void gmx_fatal_collective(int f_errno, const char *file, int line,
MPI_Comm comm, gmx_bool bMaster,
- const char *fmt, ...)
+ gmx_fmtstr const char *fmt, ...)
{
va_list ap;
gmx_bool bFinalize;
#include "gromacs/utility/basedefinitions.h"
#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/stringutil.h"
struct gmx_multisim_t;
struct t_commrec;
[[ noreturn ]] void
gmx_fatal_collective(int f_errno, const char *file, int line,
MPI_Comm comm, gmx_bool bMaster,
- const char *fmt, ...);
+ gmx_fmtstr const char *fmt, ...) gmx_format(printf, 6, 7);
/* As gmx_fatal declared in utility/fatalerror.h,
* but only the master process prints the error message.
* This should only be called one of the following two situations:
auto accelerationGroupNames = gmx::splitString(is->accgrps);
if (accelerationGroupNames.size() * DIM != accelerations.size())
{
- gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values",
+ gmx_fatal(FARGS, "Invalid Acceleration input: %zu groups and %zu acc. values",
accelerationGroupNames.size(), accelerations.size());
}
do_numbering(natoms, groups, accelerationGroupNames, grps, gnames, egcACC,
auto freezeGroupNames = gmx::splitString(is->freeze);
if (freezeDims.size() != DIM * freezeGroupNames.size())
{
- gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values",
+ gmx_fatal(FARGS, "Invalid Freezing input: %zu groups and %zu freeze values",
freezeGroupNames.size(), freezeDims.size());
}
do_numbering(natoms, groups, freezeGroupNames, grps, gnames, egcFREEZE,
}
else
{
- gmx_fatal(FARGS, "Increase MAXSLEN in the grompp code to at least %lu,"
+ gmx_fatal(FARGS, "Increase MAXSLEN in the grompp code to at least %zu,"
" or shorten your definition of bonds like %s to at most %d",
strlen(s)+1, s, MAXSLEN-1);
}
GMX_ASSERT(stat == cudaSuccess, fullMessage.c_str());
// TODO When we evolve a better logging framework, use that
// for release-build error reporting.
- gmx_warning(fullMessage.c_str());
+ gmx_warning("%s", fullMessage.c_str());
}
} // namespace
"The %s binary does not include support for the CUDA architecture "
"of the selected GPU (device ID #%d, compute capability %d.%d). "
"By default, GROMACS supports all common architectures, so your GPU "
- "might be rare, or some architectures were disabled in the build. ",
- "Consult the install guide for how to use the GMX_CUDA_TARGET_SM and ",
+ "might be rare, or some architectures were disabled in the build. "
+ "Consult the install guide for how to use the GMX_CUDA_TARGET_SM and "
"GMX_CUDA_TARGET_COMPUTE CMake variables to add this architecture.",
gmx::getProgramContext().displayName(), devInfo->id,
devInfo->prop.major, devInfo->prop.minor);
// errors during sanity checks don't propagate.
if ((stat = cudaGetLastError()) != cudaSuccess)
{
- gmx_warning(gmx::formatString("An error occurred while sanity checking device #%d; %s: %s",
- devs[i].id, cudaGetErrorName(stat), cudaGetErrorString(stat)).c_str());
+ gmx_warning("An error occurred while sanity checking device #%d; %s: %s",
+ devs[i].id, cudaGetErrorName(stat), cudaGetErrorString(stat));
}
}
}
}
#endif
GMX_RELEASE_ASSERT(status == CL_SUCCESS,
- gmx::formatString("An unexpected value was returned from clGetPlatformIDs %u: %s",
+ gmx::formatString("An unexpected value was returned from clGetPlatformIDs %d: %s",
status, ocl_get_error_string(status).c_str()).c_str());
bool foundPlatform = (numPlatforms > 0);
if (!foundPlatform && errorMessage != nullptr)
cl_int status = clGetPlatformIDs(0, nullptr, &ocl_platform_count);
if (CL_SUCCESS != status)
{
- GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %u was returned from clGetPlatformIDs: ",
+ GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %d was returned from clGetPlatformIDs: ",
status) + ocl_get_error_string(status)));
}
status = clGetPlatformIDs(ocl_platform_count, ocl_platform_ids, nullptr);
if (CL_SUCCESS != status)
{
- GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %u was returned from clGetPlatformIDs: ",
+ GMX_THROW(gmx::InternalError(gmx::formatString("An unexpected value %d was returned from clGetPlatformIDs: ",
status) + ocl_get_error_string(status)));
}
s += gmx::formatString(" Numa nodes:\n");
for (auto &n : hwTop.machine().numa.nodes)
{
- s += gmx::formatString(" Node %2d (%" PRIu64 " bytes mem):", n.id, n.memory);
+ s += gmx::formatString(" Node %2d (%zu bytes mem):", n.id, n.memory);
for (auto &l : n.logicalProcessorId)
{
s += gmx::formatString(" %3d", l);
s += gmx::formatString(" Latency:\n ");
for (std::size_t j = 0; j < hwTop.machine().numa.nodes.size(); j++)
{
- s += gmx::formatString(" %5lu", j);
+ s += gmx::formatString(" %5zu", j);
}
s += gmx::formatString("\n");
for (std::size_t i = 0; i < hwTop.machine().numa.nodes.size(); i++)
{
- s += gmx::formatString(" %5lu", i);
+ s += gmx::formatString(" %5zu", i);
for (std::size_t j = 0; j < hwTop.machine().numa.nodes.size(); j++)
{
s += gmx::formatString(" %5.2f", hwTop.machine().numa.relativeLatency[i][j]);
s += gmx::formatString(" Caches:\n");
for (auto &c : hwTop.machine().caches)
{
- s += gmx::formatString(" L%d: %" PRIu64 " bytes, linesize %d bytes, assoc. %d, shared %d ways\n",
+ s += gmx::formatString(" L%d: %zu bytes, linesize %d bytes, assoc. %d, shared %d ways\n",
c.level, c.size, c.linesize, c.associativity, c.shared);
}
}
#include "config.h"
#include <cassert>
+#include <cinttypes>
#include <climits>
#include <cstdlib>
if (gmx_debug_at)
{
#if BITMASK_SIZE <= 64 //move into bitmask when it is C++
- std::string flags = gmx::formatString("%lx", *mask);
+ std::string flags = gmx::formatString("%" PRIx64, *mask);
#else
std::string flags = gmx::formatAndJoin(*mask,
"", gmx::StringFormatter("%x"));
li_task->ntriangle++;
if (li->blnr[i+1] - li->blnr[i] > static_cast<int>(sizeof(li_task->tri_bits[0])*8 - 1))
{
- gmx_fatal(FARGS, "A constraint is connected to %d constraints, this is more than the %lu allowed for constraints participating in triangles",
+ gmx_fatal(FARGS, "A constraint is connected to %d constraints, this is more than the %zu allowed for constraints participating in triangles",
li->blnr[i+1] - li->blnr[i],
sizeof(li_task->tri_bits[0])*8-1);
}
{
gmx_fatal(FARGS, "Watch out, the input system is too large to simulate!\n"
"The number of nonbonded work units (=number of super-clusters) exceeds the"
- "device capabilities. Global work size limit exceeded (%d > %d)!",
+ "device capabilities. Global work size limit exceeded (%zu > %zu)!",
global_work_size[i], device_limit);
}
}
if (ngid*gridj->na_cj > gmx::index(sizeof(gid_cj)*8))
{
- gmx_fatal(FARGS, "The Verlet scheme with %dx%d kernels and free-energy only supports up to %lu energy groups",
+ gmx_fatal(FARGS, "The Verlet scheme with %dx%d kernels and free-energy only supports up to %zu energy groups",
gridi->na_c, gridj->na_cj, (sizeof(gid_cj)*8)/gridj->na_cj);
}
}
listSetup += "updated every ";
// Make the shortest int format string that fits nstListForSpacing
- std::string nstListFormat = "%" + gmx::formatString("%lu", gmx::formatString("%d", nstListForSpacing).size()) + "d";
+ std::string nstListFormat = "%" + gmx::formatString("%zu", gmx::formatString("%d", nstListForSpacing).size()) + "d";
listSetup += gmx::formatString(nstListFormat.c_str(), nstList);
listSetup += gmx::formatString(" steps, buffer %.3f nm, rlist %.3f nm\n",
rList - interactionCutoff, rList);
case F_ANHARM_POL:
if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10))
{
- gmx_fatal(FARGS, "polarize can not be used with qA(%e) != qB(%e) for atom %d of molecule block %lu", qS, atom[aS].qB, aS+1, mb+1);
+ gmx_fatal(FARGS, "polarize can not be used with qA(%e) != qB(%e) for atom %d of molecule block %zu", qS, atom[aS].qB, aS+1, mb+1);
}
shell[nsi].k += gmx::square(qS)*ONE_4PI_EPS0/
ffparams->iparams[type].polarize.alpha;
case F_WATER_POL:
if (!gmx_within_tol(qS, atom[aS].qB, GMX_REAL_EPS*10))
{
- gmx_fatal(FARGS, "water_pol can not be used with qA(%e) != qB(%e) for atom %d of molecule block %lu", qS, atom[aS].qB, aS+1, mb+1);
+ gmx_fatal(FARGS, "water_pol can not be used with qA(%e) != qB(%e) for atom %d of molecule block %zu", qS, atom[aS].qB, aS+1, mb+1);
}
alpha = (ffparams->iparams[type].wpol.al_x+
ffparams->iparams[type].wpol.al_y+
#include "config.h"
+#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstdlib>
{
if (!rerun_fr.bBox)
{
- gmx_fatal(FARGS, "Rerun trajectory frame step %ld time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time);
+ gmx_fatal(FARGS, "Rerun trajectory frame step %" PRId64 " time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time);
}
if (max_cutoff2(ir->ePBC, rerun_fr.box) < gmx::square(fr->rlist))
{
- gmx_fatal(FARGS, "Rerun trajectory frame step %ld time %f has too small box dimensions", rerun_fr.step, rerun_fr.time);
+ gmx_fatal(FARGS, "Rerun trajectory frame step %" PRId64 " time %f has too small box dimensions", rerun_fr.step, rerun_fr.time);
}
}
}
#include "config.h"
#include <cassert>
+#include <cinttypes>
#include <csignal>
#include <cstdlib>
#include <cstring>
}
else if (nsteps_cmdline < -2)
{
- gmx_fatal(FARGS, "Invalid nsteps value passed on the command line: %ld",
+ gmx_fatal(FARGS, "Invalid nsteps value passed on the command line: %" PRId64,
nsteps_cmdline);
}
/* Do nothing if nsteps_cmdline == -2 */
#include "config.h"
#include <cassert>
+#include <cinttypes>
#include <cmath>
#include <cstdio>
#include <cstdlib>
if (dnorm2(r_ij[c]) == 0)
{
- gmx_fatal(FARGS, "Distance for pull coordinate %lu is zero with constraint pulling, which is not allowed.", c + 1);
+ gmx_fatal(FARGS, "Distance for pull coordinate %zu is zero with constraint pulling, which is not allowed.", c + 1);
}
}
#include "gromacs/utility/arraysize.h"
#include "gromacs/utility/cstringutil.h"
#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
#include "selmethod-impl.h"
#include "symrec.h"
* Convenience function for reporting errors found in selection methods.
*/
static void
-report_error(FILE *fp, const char *name, const char *fmt, ...)
+report_error(FILE *fp, const char *name, gmx_fmtstr const char *fmt, ...) gmx_format(printf, 3, 4);
+
+static void
+report_error(FILE *fp, const char *name, gmx_fmtstr const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
*/
static void
report_param_error(FILE *fp, const char *mname, const char *pname,
- const char *fmt, ...)
+ gmx_fmtstr const char *fmt, ...) gmx_format(printf, 4, 5);
+static void
+report_param_error(FILE *fp, const char *mname, const char *pname,
+ gmx_fmtstr const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
}
if (!isConsistent)
{
- GMX_THROW(InconsistentInputError(formatString("Derivative inconsistent with numerical vector for elements %lu-%lu", minFail+1, maxFail+1)));
+ GMX_THROW(InconsistentInputError(formatString("Derivative inconsistent with numerical vector for elements %zu-%zu", minFail+1, maxFail+1)));
}
}
gmx_exit_on_fatal_error(exitType, 1);
}
-void gmx_fatal(int f_errno, const char *file, int line, const char *fmt, ...)
+void gmx_fatal(int f_errno, const char *file, int line, gmx_fmtstr const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
}
}
-void gmx_warning(const char *fmt, ...)
+void gmx_warning(gmx_fmtstr const char *fmt, ...)
{
va_list ap;
char msg[STRLEN];
#include <stdio.h>
#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/stringutil.h"
/*! \brief
* Debug log file.
\endcode
*/
[[noreturn]] void
-gmx_fatal(int fatal_errno, const char *file, int line, const char *fmt, ...);
+gmx_fatal(int fatal_errno, const char *file, int line, gmx_fmtstr const char *fmt, ...) gmx_format(printf, 4, 5);
/** Helper macro to pass first three parameters to gmx_fatal(). */
#define FARGS 0, __FILE__, __LINE__
* The message string should NOT start with "WARNING"
* and should NOT end with a newline.
*/
-void gmx_warning(const char *fmt, ...);
+void gmx_warning(gmx_fmtstr const char *fmt, ...) gmx_format(printf, 1, 2);
#endif
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2016, by the GROMACS development team, led by
+ * Copyright (c) 2016,2018, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
}
-LogEntryWriter &LogEntryWriter::appendTextFormatted(const char *fmt, ...)
+LogEntryWriter &LogEntryWriter::appendTextFormatted(gmx_fmtstr const char *fmt, ...)
{
va_list ap;
#include <string>
+#include "gromacs/utility/stringutil.h"
+
namespace gmx
{
return *this;
}
//! Appends given text as a line in the log entry, with printf-style formatting.
- LogEntryWriter &appendTextFormatted(const char *fmt, ...);
+ LogEntryWriter &appendTextFormatted(gmx_fmtstr const char *fmt, ...) gmx_format(printf, 2, 3);
//! Writes the log entry with empty lines before and after.
LogEntryWriter &asParagraph()
{
return std::string(start, end);
}
-std::string formatString(const char *fmt, ...)
+std::string formatString(gmx_fmtstr const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2011,2012,2013,2014,2015,2016,2017, by the GROMACS development team, led by
+ * Copyright (c) 2011,2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
* \throws std::bad_alloc if out of memory.
*/
std::string stripString(const std::string &str);
+#ifdef __GNUC__
+#define gmx_format(archetype, string_index, first_to_check) \
+ __attribute__ ((format (archetype, string_index, first_to_check)))
+#else
+/*! \brief GCC like function format attribute
+ *
+ * The format attribute specifies that a function takes printf, scanf, ...
+ * style arguments that should be type-checked against a format string.
+ * The attribute has to be placed after the function.
+ * This attribute is only valid for function declarations and not function
+ * definitions (GCC limitation). For member functions the implicit `this`
+ * pointer is included in the argument count.
+ */
+#define gmx_format(archetype, string_index, first_to_check)
+#endif
+#ifdef _MSC_VER
+#define gmx_fmtstr _In_ _Printf_format_string_
+#else
+/*! \brief MSVC like function format attribute
+ *
+ * Does type checking for printf like format strings in MSVC style.
+ * Attribute has to be placed before format string.
+ */
+#define gmx_fmtstr
+#endif
/*! \brief
* Formats a string (snprintf() wrapper).
*
* instead of requiring a preallocated buffer. Arbitrary length output is
* supported.
*/
-std::string formatString(const char *fmt, ...);
+std::string formatString(gmx_fmtstr const char *fmt, ...) gmx_format(printf, 1, 2);
+
/*! \brief
* Formats a string (vsnprintf() wrapper).
*