I got busy and implemented the code to output messages 'lazily' so that
the message can be defined before the locale directory has been
ascertained, but only output afterwards. The resulting code 'in use' looks
so:
int main()
{
lazy::messages()[debug_trait::WARN] +=
lazy::messages().err() << 22 << ' ' << 33 << std::endl;
lazy::messages() +=
lazy::messages().err() << lazy::tr("some translated text")
<< std::endl;
// string const localedir = ...;
// gettext_init(localedir);
// Flush what is stored through to the stream itself.
// Ie, invoke the phoenix actor's operator() which
// causes the lazy function to get it's ass into gear.
lazy::messages().flush();
}
Ie, the "err() << foo << bar" is a weird and wonderful type which can be
stored in a boost::function variable and so added to a
vector<boost::function> store. Once the locale directory has been found
then the message is output by invoking the boost::function's operator().
The working example code is attached. Try it out with
$ g++ -Ilyx/devel/boost -o lazy lazy.cpp
$ ./lazy
IMO, LyX 1.4.x needs this mechanism up to this point in the execution
(below). Ie, only the easyParse and init_package code needs to use it. The
lazy::messages().err() 'lazy stream' will be initialised with lyxerr.
int main(int argc, char * argv[])
{
// To avoid ordering of global object problems with some
// stdlibs we do the initialization here, but still as
// early as possible.
lyxerr.rdbuf(std::cerr.rdbuf());
// initialize for internationalized version *EK*
locale_init();
LyX::exec(argc, argv);
return 0;
}
void LyX::exec(int & argc, char * argv[])
{
static bool initialised = false;
BOOST_ASSERT(!initialised);
initialised = true;
static LyX singleton;
singlton.priv_exec(argc, argv);
}
void LyX::priv_exec(int & argc, char * argv[])
{
// Here we need to parse the command line. At least
// we need to parse for "-dbg" and "-help"
bool const want_gui = easyParse(argc, argv);
lyx::support::init_package(argv[0],
cl_system_support,
cl_user_support);
// Set the locale_dir.
string const & locale_dir = package().locale_dir();
FileInfo fi(locale_dir);
if (fi.isOK() && fi.isDir()) {
// No real need to output this message 'lazily' because
// it's not translated. Doing so, however, ensures that
// messages are output in the correct order.
lazy::messages()[Debug::INIT] +=
lazy::messages().err() << "Setting locale directory to "
<< locale_dir << endl;
// This needs to be defined for LyX 1.4.x.
// gettext_init(locale_dir);
}
lazy::messages().flush();
// if LyX was invoked as "lyx --help" then exit now.
...
// From here on in, use the normal lyxerr way.
...
}
--
Angus
/**
* \file lazy.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Angus Leeming
*
* Full author contact details are available in file CREDITS.
*
* LyX suffers from something of a "chicken and egg" situation when
* it first starts. Information, warning and error messages can be
* output before the path to the locale directory has been ascertained
* and gettext has been initialised. These messages, will, therefore
* be output in English. Which is fine if you speak English and less
* fine if you don't.
*
* The solution is to output these messages 'lazily'. Ie, to store the
* messages that should be output once gettext has been initialized and
* to the 'flush' out these stored messages
*
* At the implementation level, this solution uses the Phoenix library
* that is bundled with Boost.Spirit. Phoenix splits the definition
* of the action (message) from its execution (output of message).
*/
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/spirit/phoenix.hpp>
#include <iostream>
#include <string>
#include <vector>
using std::string;
// Dummy gettext routine. Just wraps the input in 'I18N' blocks
string const tr(string const & data)
{
return string("<I18N>") + data + "<\\I18N>";
}
struct debug_trait {
enum type {
NONE = 0,
EMERG = 1,
ALERT = 2,
CRIT = 3,
ERR = 4,
WARN = 5,
NOTICE = 6,
INFO = 7,
DEBUG = 8,
ANY = 0xffffff
};
static bool match(type a, type b) {
return (b <= a || (b == ANY && a > NONE));
}
};
namespace lazy {
// message_storage is a singleton class.
struct message_storage : boost::noncopyable
{
/** Store the accumulation of "err() << foo << bar" as a function
* to be evaluated later.
*/
typedef boost::function<void ()> function_type;
/// The lazy stream
typedef phoenix::actor<phoenix::variable<std::ostream> > stream_type;
/** Add a function call to the store of function calls
* that will be made when @c flush() is called.
*/
void operator+=(function_type const & data)
{
data_.push_back(data);
}
/** Accessor to the lazy stream variable.
* Note that "err() << foo;" doesn't actually do anything (the returned
* type is const after all).
* However "err() << foo" is a type in it's own right. Calling the
* function operator of this type will evaluate the expression.
*/
stream_type const & err() const
{
return err_;
}
/** Invoke each function in the store of functions.
* Thereafter, erase the store.
*/
void flush()
{
container_t::const_iterator it = data_.begin();
container_t::const_iterator const end = data_.end();
for (; it != end; ++it)
(*it)();
data_.clear();
}
/** Accessors to set and get the debug level at which messages
* are to be discarded.
*/
void level(debug_trait::type t) { t_ = t; }
debug_trait::type level() const { return t_; }
/** A variable of this type is returned from message_store::operator[]
* It enables messages to be discarded if the message debug level
* is lower than that of the message_store.
*/
struct proxy
{
proxy(message_storage & parent, debug_trait::type t)
: parent_(parent), t_(t) {}
void operator+=(function_type const & data)
{
if (debug_trait::match(parent_.level(), t_))
parent_ += data;
}
private:
message_storage & parent_;
debug_trait::type t_;
};
proxy operator[](debug_trait::type t) { return proxy(*this, t); }
private:
friend message_storage & messages();
message_storage()
: err_(phoenix::var(std::cerr)),
t_(debug_trait::NONE)
{}
stream_type err_;
debug_trait::type t_;
typedef std::vector<function_type> container_t;
container_t data_;
};
// Accessor to the single message_storage variable.
message_storage & messages()
{
static message_storage store;
return store;
}
// struct to invoke gettext lazily (when it's function operator is called).
struct gettext_impl {
template <typename Arg>
struct result {
typedef string const type;
};
string const operator()(string const & data) const
{
return tr(data);
}
};
phoenix::function<gettext_impl> const tr = gettext_impl();
} // namespace lazy
int main()
{
lazy::messages()[debug_trait::WARN] +=
lazy::messages().err() << 22 << ' ' << 33 << std::endl;
lazy::messages() +=
lazy::messages().err() << lazy::tr("some translated text") << std::endl;
// string const localedir = ...;
// gettext_init(localedir);
// Flush what is stored through to the stream itself.
// Ie, invoke the phoenix actor's operator() which
// causes the lazy function to get it's ass into gear.
lazy::messages().flush();
}