Use a "format accumulator" for error strings

This makes it possible to internationalize strings while still using
I/O streams.  For example:

    std::cout << ACCUM(_("Hello to %1 and %2!") << "me" << "you")
              << std::endl;
This commit is contained in:
John Wiegley 2009-02-21 19:52:31 -04:00
parent a577e8c48e
commit 66c5cd4427
5 changed files with 187 additions and 4 deletions

View file

@ -18,6 +18,7 @@ libledger_util_la_SOURCES = \
src/times.cc \
src/error.cc \
src/utils.cc \
src/accum.cc \
lib/sha1.cpp
libledger_util_la_CPPFLAGS = $(lib_cppflags)
@ -86,6 +87,7 @@ pkginclude_HEADERS = \
src/stream.h \
src/pstream.h \
src/unistring.h \
src/accum.h \
\
src/amount.h \
src/commodity.h \

70
src/accum.cc Normal file
View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "utils.h"
namespace ledger {
std::streamsize straccbuf::xsputn(const char * s, std::streamsize num)
{
if (index == 0) {
// The first item received is the format string
str = std::string(s, num);
index++;
return num;
}
else {
std::ostringstream buf;
// Every item thereafter is an argument that substitutes for %# in the
// format string
for (const char * p = str.c_str(); *p; p++) {
if (*p == '%') {
const char * q = p + 1;
if (*q && *q != '%' && std::isdigit(*q) &&
std::size_t(*q - '0') == index) {
p++;
buf << std::string(s, num);
} else {
buf << *p;
}
} else {
buf << *p;
}
}
str = buf.str();
index++;
return num;
}
}
} // namespace ledger

100
src/accum.h Normal file
View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @addtogroup util
*/
/**
* @file accum.h
* @author John Wiegley
*
* @ingroup util
*
* @brief Brief
*
* Full.
*/
#ifndef _ACCUM_H
#define _ACCUM_H
namespace ledger {
/**
* @brief Brief
*
* Full.
*/
class straccbuf : public std::streambuf
{
protected:
std::string str; // accumulator
std::size_t index;
public:
straccbuf() : index(0) {}
protected:
virtual std::streamsize xsputn(const char * s, std::streamsize num);
friend class straccstream;
};
/**
* @brief Brief
*
* Full.
*/
class straccstream : public std::ostream
{
protected:
straccbuf buf;
public:
straccstream() : std::ostream(0) {
rdbuf(&buf);
}
void clear() {
buf.str.clear();
buf.index = 0;
}
std::string str() const {
return buf.str;
}
};
#define ACCUM(obj) (static_cast<straccstream&>(obj).str())
} // namespace ledger
#endif // _ACCUM_H

View file

@ -33,8 +33,10 @@
namespace ledger {
std::ostringstream _desc_buffer;
straccstream _ctxt_accum;
std::ostringstream _ctxt_buffer;
straccstream _desc_accum;
std::ostringstream _desc_buffer;
string error_context()
{

View file

@ -46,8 +46,11 @@
#ifndef _ERROR_H
#define _ERROR_H
#include "accum.h"
namespace ledger {
extern straccstream _desc_accum;
extern std::ostringstream _desc_buffer;
template <typename T>
@ -56,14 +59,20 @@ inline void throw_func(const string& message) {
throw T(message);
}
#define throw_(cls, msg) \
((_desc_buffer << msg), throw_func<cls>(_desc_buffer.str()))
#define throw_(cls, msg) \
((_desc_buffer << ACCUM(_desc_accum << msg)), \
_desc_accum.clear(), \
throw_func<cls>(_desc_buffer.str()))
extern straccstream _ctxt_accum;
extern std::ostringstream _ctxt_buffer;
#define add_error_context(msg) \
((long(_ctxt_buffer.tellp()) == 0) ? \
(_ctxt_buffer << msg) : (_ctxt_buffer << std::endl << msg))
((_ctxt_buffer << ACCUM(_ctxt_accum << msg)), \
_ctxt_accum.clear()) : \
((_ctxt_buffer << std::endl << ACCUM(_ctxt_accum << msg)), \
_ctxt_accum.clear()))
string error_context();