2015-08-07 21:14:21 +03:00
|
|
|
// -*- mode: c++; indent-tabs-mode: nil; tab-width: 2 -*-
|
2015-02-05 22:35:49 +03:00
|
|
|
// $Id: ExportInterface.cpp 3045 2010-04-05 13:07:29Z hieuhoang1972 $
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Moses - factored phrase-based language decoder
|
|
|
|
Copyright (C) 2009 University of Edinburgh
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Moses interface for main function, for single-threaded and multi-threaded.
|
|
|
|
**/
|
|
|
|
#include <exception>
|
|
|
|
#include <fstream>
|
|
|
|
#include <sstream>
|
|
|
|
#include <vector>
|
|
|
|
|
2015-04-23 15:27:21 +03:00
|
|
|
#include "util/random.hh"
|
2015-02-05 22:35:49 +03:00
|
|
|
#include "util/usage.hh"
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
// Include Visual Leak Detector
|
|
|
|
//#include <vld.h>
|
|
|
|
#endif
|
|
|
|
|
2015-03-07 02:37:07 +03:00
|
|
|
|
2015-02-05 22:35:49 +03:00
|
|
|
#include "Hypothesis.h"
|
|
|
|
#include "Manager.h"
|
|
|
|
#include "StaticData.h"
|
|
|
|
#include "TypeDef.h"
|
|
|
|
#include "Util.h"
|
|
|
|
#include "Timer.h"
|
|
|
|
#include "TranslationModel/PhraseDictionary.h"
|
|
|
|
#include "FF/StatefulFeatureFunction.h"
|
|
|
|
#include "FF/StatelessFeatureFunction.h"
|
|
|
|
#include "TranslationTask.h"
|
2015-03-07 02:37:07 +03:00
|
|
|
#include "ExportInterface.h"
|
2015-02-05 22:35:49 +03:00
|
|
|
|
|
|
|
#ifdef HAVE_PROTOBUF
|
|
|
|
#include "hypergraph.pb.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PT_UG
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
#include "TranslationModel/UG/mmsapt.h"
|
|
|
|
#include "TranslationModel/UG/generic/program_options/ug_splice_arglist.h"
|
|
|
|
#endif
|
|
|
|
|
2015-03-07 02:37:07 +03:00
|
|
|
#include "ExportInterface.h"
|
2015-11-06 17:44:52 +03:00
|
|
|
|
|
|
|
#ifdef HAVE_XMLRPC_C
|
2015-11-04 21:12:49 +03:00
|
|
|
#include "moses/server/Server.h"
|
2015-11-06 17:44:52 +03:00
|
|
|
#endif
|
2015-12-07 19:01:12 +03:00
|
|
|
#include <signal.h>
|
2015-11-06 17:44:52 +03:00
|
|
|
|
2015-02-05 22:35:49 +03:00
|
|
|
using namespace std;
|
|
|
|
using namespace Moses;
|
|
|
|
|
|
|
|
namespace Moses
|
|
|
|
{
|
2015-11-10 19:27:28 +03:00
|
|
|
//extern size_t g_numHypos;
|
2015-11-06 14:51:14 +03:00
|
|
|
|
2015-02-05 22:35:49 +03:00
|
|
|
void OutputFeatureWeightsForHypergraph(std::ostream &outputSearchGraphStream)
|
|
|
|
{
|
|
|
|
outputSearchGraphStream.setf(std::ios::fixed);
|
|
|
|
outputSearchGraphStream.precision(6);
|
|
|
|
StaticData::Instance().GetAllWeights().Save(outputSearchGraphStream);
|
|
|
|
}
|
2015-03-12 00:43:48 +03:00
|
|
|
} //namespace Moses
|
2015-03-07 02:37:07 +03:00
|
|
|
|
|
|
|
SimpleTranslationInterface::SimpleTranslationInterface(const string &mosesIni): m_staticData(StaticData::Instance())
|
|
|
|
{
|
2015-05-02 13:45:24 +03:00
|
|
|
if (!m_params.LoadParam(mosesIni)) {
|
|
|
|
cerr << "Error; Cannot load parameters at " << mosesIni<<endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
2015-11-10 17:01:05 +03:00
|
|
|
ResetUserTime();
|
2015-05-02 13:45:24 +03:00
|
|
|
if (!StaticData::LoadDataStatic(&m_params, mosesIni.c_str())) {
|
|
|
|
cerr << "Error; Cannot load static data in file " << mosesIni<<endl;
|
|
|
|
exit(1);
|
|
|
|
}
|
2015-03-07 02:37:07 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
util::rand_init();
|
2015-03-07 02:37:07 +03:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SimpleTranslationInterface::~SimpleTranslationInterface()
|
|
|
|
{}
|
|
|
|
|
|
|
|
//the simplified version of string input/output translation
|
|
|
|
string SimpleTranslationInterface::translate(const string &inputString)
|
|
|
|
{
|
2015-12-10 06:17:36 +03:00
|
|
|
boost::shared_ptr<Moses::IOWrapper> ioWrapper(new IOWrapper(*StaticData::Instance().options()));
|
2015-03-07 02:37:07 +03:00
|
|
|
// main loop over set of input sentences
|
|
|
|
size_t sentEnd = inputString.rfind('\n'); //find the last \n, the input stream has to be appended with \n to be translated
|
|
|
|
const string &newString = sentEnd != string::npos ? inputString : inputString + '\n';
|
|
|
|
|
|
|
|
istringstream inputStream(newString); //create the stream for the interface
|
2015-03-21 19:12:52 +03:00
|
|
|
ioWrapper->SetInputStreamFromString(inputStream);
|
2015-03-07 02:37:07 +03:00
|
|
|
ostringstream outputStream;
|
2015-03-21 19:12:52 +03:00
|
|
|
ioWrapper->SetOutputStream2SingleBestOutputCollector(&outputStream);
|
2015-03-07 02:37:07 +03:00
|
|
|
|
2015-03-21 19:12:52 +03:00
|
|
|
boost::shared_ptr<InputType> source = ioWrapper->ReadInput();
|
|
|
|
if (!source) return "Error: Source==null!!!";
|
2015-05-02 13:45:24 +03:00
|
|
|
IFVERBOSE(1) {
|
|
|
|
ResetUserTime();
|
|
|
|
}
|
2015-03-21 19:12:52 +03:00
|
|
|
|
|
|
|
// set up task of translating one sentence
|
2015-04-30 08:05:11 +03:00
|
|
|
boost::shared_ptr<TranslationTask> task
|
2015-05-02 13:45:24 +03:00
|
|
|
= TranslationTask::create(source, ioWrapper);
|
2015-03-21 19:12:52 +03:00
|
|
|
task->Run();
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-03-21 19:12:52 +03:00
|
|
|
string output = outputStream.str();
|
|
|
|
//now trim the end whitespace
|
|
|
|
const string whitespace = " \t\f\v\n\r";
|
|
|
|
size_t end = output.find_last_not_of(whitespace);
|
2015-03-07 02:37:07 +03:00
|
|
|
return output.erase(end + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Moses::StaticData& SimpleTranslationInterface::getStaticData()
|
|
|
|
{
|
|
|
|
return StaticData::InstanceNonConst();
|
|
|
|
}
|
|
|
|
void SimpleTranslationInterface::DestroyFeatureFunctionStatic()
|
|
|
|
{
|
|
|
|
FeatureFunction::Destroy();
|
|
|
|
}
|
2015-02-05 22:35:49 +03:00
|
|
|
|
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
Parameter params;
|
2015-02-05 22:35:49 +03:00
|
|
|
|
2015-12-09 03:00:35 +03:00
|
|
|
void
|
2015-12-07 19:01:12 +03:00
|
|
|
signal_handler(int signum)
|
|
|
|
{
|
|
|
|
if (signum == SIGALRM) {
|
|
|
|
exit(0); // that's what we expected from the child process after forking
|
2015-12-09 03:00:35 +03:00
|
|
|
} else if (signum == SIGTERM || signum == SIGKILL) {
|
2015-12-07 19:01:12 +03:00
|
|
|
exit(0);
|
2015-12-09 03:00:35 +03:00
|
|
|
} else {
|
2015-12-07 19:01:12 +03:00
|
|
|
std::cerr << "Unexpected signal " << signum << std::endl;
|
2015-12-09 03:00:35 +03:00
|
|
|
exit(signum);
|
2015-12-07 19:01:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
//! run moses in server mode
|
|
|
|
int
|
|
|
|
run_as_server()
|
|
|
|
{
|
2015-08-08 15:27:56 +03:00
|
|
|
#ifdef HAVE_XMLRPC_C
|
2016-01-12 00:43:58 +03:00
|
|
|
if (params.GetParam("daemon")) {
|
|
|
|
kill(getppid(),SIGALRM);
|
|
|
|
}
|
2015-08-04 03:59:28 +03:00
|
|
|
MosesServer::Server server(params);
|
|
|
|
return server.run(); // actually: don't return. see Server::run()
|
2015-11-04 21:12:49 +03:00
|
|
|
#else
|
|
|
|
UTIL_THROW2("Moses was compiled without xmlrpc-c. "
|
|
|
|
<< "No server functionality available.");
|
2015-08-08 15:27:56 +03:00
|
|
|
#endif
|
2015-03-04 03:07:11 +03:00
|
|
|
}
|
2015-02-05 22:35:49 +03:00
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
int
|
|
|
|
batch_run()
|
|
|
|
{
|
|
|
|
// shorthand for accessing information in StaticData
|
|
|
|
const StaticData& staticData = StaticData::Instance();
|
|
|
|
|
|
|
|
//initialise random numbers
|
2015-04-23 15:27:21 +03:00
|
|
|
util::rand_init();
|
2015-03-04 03:07:11 +03:00
|
|
|
|
|
|
|
IFVERBOSE(1) PrintUserTime("Created input-output object");
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-03-21 19:12:52 +03:00
|
|
|
// set up read/writing class:
|
2015-12-10 06:17:36 +03:00
|
|
|
boost::shared_ptr<IOWrapper> ioWrapper(new IOWrapper(*staticData.options()));
|
2015-03-04 03:07:11 +03:00
|
|
|
UTIL_THROW_IF2(ioWrapper == NULL, "Error; Failed to create IO object"
|
2015-05-02 13:45:24 +03:00
|
|
|
<< " [" << HERE << "]");
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
// check on weights
|
|
|
|
const ScoreComponentCollection& weights = staticData.GetAllWeights();
|
2015-05-02 13:45:24 +03:00
|
|
|
IFVERBOSE(2) {
|
|
|
|
TRACE_ERR("The global weight vector looks like this: ");
|
|
|
|
TRACE_ERR(weights);
|
|
|
|
TRACE_ERR("\n");
|
|
|
|
}
|
2015-02-05 22:35:49 +03:00
|
|
|
|
|
|
|
#ifdef WITH_THREADS
|
2015-03-04 03:07:11 +03:00
|
|
|
ThreadPool pool(staticData.ThreadCount());
|
2015-02-05 22:35:49 +03:00
|
|
|
#endif
|
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
// using context for adaptation:
|
2015-08-08 02:00:45 +03:00
|
|
|
// e.g., context words / strings from config file / cmd line
|
|
|
|
std::string context_string;
|
2015-04-03 18:16:52 +03:00
|
|
|
params.SetParameter(context_string,"context-string",string(""));
|
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
// ... or weights for documents/domains from config file / cmd. line
|
2015-06-23 17:02:15 +03:00
|
|
|
std::string context_weights;
|
|
|
|
params.SetParameter(context_weights,"context-weights",string(""));
|
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
// ... or the surrounding context (--context-window ...)
|
|
|
|
size_t size_t_max = std::numeric_limits<size_t>::max();
|
|
|
|
bool use_context_window = ioWrapper->GetLookAhead() || ioWrapper->GetLookBack();
|
2015-11-29 20:56:27 +03:00
|
|
|
// bool use_context = use_context_window || context_string.size();
|
2015-08-08 02:00:45 +03:00
|
|
|
bool use_sliding_context_window = (use_context_window
|
|
|
|
&& ioWrapper->GetLookAhead() != size_t_max);
|
2015-03-21 19:12:52 +03:00
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
boost::shared_ptr<std::vector<std::string> > context_window;
|
|
|
|
boost::shared_ptr<std::vector<std::string> >* cw;
|
|
|
|
cw = use_context_window ? &context_window : NULL;
|
2015-08-08 02:00:45 +03:00
|
|
|
if (!cw && context_string.size())
|
2015-07-21 17:35:08 +03:00
|
|
|
context_window.reset(new std::vector<std::string>(1,context_string));
|
2015-06-08 13:44:03 +03:00
|
|
|
|
|
|
|
// global scope of caches, biases, etc., if any
|
|
|
|
boost::shared_ptr<ContextScope> gscope;
|
2015-08-08 02:00:45 +03:00
|
|
|
if (!use_sliding_context_window)
|
2015-06-08 13:44:03 +03:00
|
|
|
gscope.reset(new ContextScope);
|
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
// main loop over set of input sentences
|
|
|
|
boost::shared_ptr<InputType> source;
|
|
|
|
while ((source = ioWrapper->ReadInput(cw)) != NULL) {
|
2015-05-02 13:45:24 +03:00
|
|
|
IFVERBOSE(1) ResetUserTime();
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
// set up task of translating one sentence
|
2015-06-08 13:44:03 +03:00
|
|
|
boost::shared_ptr<ContextScope> lscope;
|
|
|
|
if (gscope) lscope = gscope;
|
|
|
|
else lscope.reset(new ContextScope);
|
2015-08-08 02:00:45 +03:00
|
|
|
|
|
|
|
boost::shared_ptr<TranslationTask> task;
|
2015-06-08 13:44:03 +03:00
|
|
|
task = TranslationTask::create(source, ioWrapper, lscope);
|
2015-08-08 02:00:45 +03:00
|
|
|
|
|
|
|
if (cw) {
|
|
|
|
if (context_string.size())
|
|
|
|
context_window->push_back(context_string);
|
|
|
|
if(!use_sliding_context_window)
|
|
|
|
cw = NULL;
|
|
|
|
}
|
2015-10-28 13:26:41 +03:00
|
|
|
|
2015-07-21 17:35:08 +03:00
|
|
|
if (context_window)
|
|
|
|
task->SetContextWindow(context_window);
|
2015-10-31 20:14:47 +03:00
|
|
|
|
2015-10-28 13:26:41 +03:00
|
|
|
if (context_weights != "" && !task->GetScope()->GetContextWeights())
|
|
|
|
task->GetScope()->SetContextWeights(context_weights);
|
2015-10-31 20:14:47 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
// Allow for (sentence-)context-specific processing prior to
|
|
|
|
// decoding. This can be used, for example, for context-sensitive
|
|
|
|
// phrase lookup.
|
|
|
|
FeatureFunction::SetupAll(*task);
|
2015-03-21 20:09:41 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
// execute task
|
2015-02-05 22:35:49 +03:00
|
|
|
#ifdef WITH_THREADS
|
|
|
|
#ifdef PT_UG
|
2015-05-02 13:45:24 +03:00
|
|
|
// simulated post-editing requires threads (within the dynamic phrase tables)
|
|
|
|
// but runs all sentences serially, to allow updating of the bitext.
|
|
|
|
bool spe = params.isParamSpecified("spe-src");
|
|
|
|
if (spe) {
|
|
|
|
// simulated post-editing: always run single-threaded!
|
|
|
|
task->Run();
|
|
|
|
string src,trg,aln;
|
|
|
|
UTIL_THROW_IF2(!getline(*ioWrapper->spe_src,src), "[" << HERE << "] "
|
|
|
|
<< "missing update data for simulated post-editing.");
|
|
|
|
UTIL_THROW_IF2(!getline(*ioWrapper->spe_trg,trg), "[" << HERE << "] "
|
|
|
|
<< "missing update data for simulated post-editing.");
|
|
|
|
UTIL_THROW_IF2(!getline(*ioWrapper->spe_aln,aln), "[" << HERE << "] "
|
|
|
|
<< "missing update data for simulated post-editing.");
|
|
|
|
BOOST_FOREACH (PhraseDictionary* pd, PhraseDictionary::GetColl()) {
|
|
|
|
Mmsapt* sapt = dynamic_cast<Mmsapt*>(pd);
|
|
|
|
if (sapt) sapt->add(src,trg,aln);
|
|
|
|
VERBOSE(1,"[" << HERE << " added src] " << src << endl);
|
|
|
|
VERBOSE(1,"[" << HERE << " added trg] " << trg << endl);
|
|
|
|
VERBOSE(1,"[" << HERE << " added aln] " << aln << endl);
|
|
|
|
}
|
|
|
|
} else pool.Submit(task);
|
2015-03-04 03:07:11 +03:00
|
|
|
#else
|
2015-05-02 13:45:24 +03:00
|
|
|
pool.Submit(task);
|
2015-03-04 03:07:11 +03:00
|
|
|
|
2015-02-05 22:35:49 +03:00
|
|
|
#endif
|
|
|
|
#else
|
2015-05-02 13:45:24 +03:00
|
|
|
task->Run();
|
2015-02-05 22:35:49 +03:00
|
|
|
#endif
|
2015-05-02 13:45:24 +03:00
|
|
|
}
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
// we are done, finishing up
|
2015-02-05 22:35:49 +03:00
|
|
|
#ifdef WITH_THREADS
|
2015-03-04 03:07:11 +03:00
|
|
|
pool.Stop(true); //flush remaining jobs
|
2015-02-05 22:35:49 +03:00
|
|
|
#endif
|
|
|
|
|
2015-11-10 19:27:28 +03:00
|
|
|
// cerr << "g_numHypos=" << Moses::g_numHypos << endl;
|
2015-11-09 15:15:22 +03:00
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
FeatureFunction::Destroy();
|
2015-02-05 22:35:49 +03:00
|
|
|
|
2016-06-29 23:32:21 +03:00
|
|
|
IFVERBOSE(0) util::PrintUsage(std::cerr);
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-02-05 22:35:49 +03:00
|
|
|
#ifndef EXIT_RETURN
|
|
|
|
//This avoids that destructors are called (it can take a long time)
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
#else
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-03-04 03:07:11 +03:00
|
|
|
/** Called by main function of the command line version of the decoder **/
|
2015-11-11 02:05:51 +03:00
|
|
|
int decoder_main(int argc, char const** argv)
|
2015-03-04 03:07:11 +03:00
|
|
|
{
|
2015-04-29 22:07:03 +03:00
|
|
|
#ifdef NDEBUG
|
|
|
|
try
|
|
|
|
#endif
|
2015-05-02 13:45:24 +03:00
|
|
|
{
|
2015-03-04 03:07:11 +03:00
|
|
|
#ifdef HAVE_PROTOBUF
|
2015-05-02 13:45:24 +03:00
|
|
|
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
2015-03-04 03:07:11 +03:00
|
|
|
#endif
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
// echo command line, if verbose
|
|
|
|
IFVERBOSE(1) {
|
|
|
|
TRACE_ERR("command: ");
|
|
|
|
for(int i=0; i<argc; ++i) TRACE_ERR(argv[i]<<" ");
|
|
|
|
TRACE_ERR(endl);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set number of significant decimals in output
|
|
|
|
FixPrecision(cout);
|
|
|
|
FixPrecision(cerr);
|
2015-04-30 08:05:11 +03:00
|
|
|
|
2015-05-02 13:45:24 +03:00
|
|
|
// load all the settings into the Parameter class
|
|
|
|
// (stores them as strings, or array of strings)
|
2015-11-11 17:04:53 +03:00
|
|
|
if (!params.LoadParam(argc,argv))
|
|
|
|
exit(1);
|
2015-05-02 13:45:24 +03:00
|
|
|
|
|
|
|
// initialize all "global" variables, which are stored in StaticData
|
|
|
|
// note: this also loads models such as the language model, etc.
|
2015-11-10 17:01:05 +03:00
|
|
|
ResetUserTime();
|
2015-05-02 13:45:24 +03:00
|
|
|
if (!StaticData::LoadDataStatic(¶ms, argv[0]))
|
|
|
|
exit(1);
|
|
|
|
|
2015-12-07 19:01:12 +03:00
|
|
|
//
|
|
|
|
#if 1
|
|
|
|
pid_t pid;
|
|
|
|
if (params.GetParam("daemon")) {
|
|
|
|
pid = fork();
|
2015-12-09 03:00:35 +03:00
|
|
|
if (pid) {
|
|
|
|
pause(); // parent process
|
|
|
|
exit(0);
|
|
|
|
}
|
2015-12-07 19:01:12 +03:00
|
|
|
}
|
|
|
|
#endif
|
2015-05-02 13:45:24 +03:00
|
|
|
// setting "-show-weights" -> just dump out weights and exit
|
|
|
|
if (params.isParamSpecified("show-weights")) {
|
|
|
|
ShowWeights();
|
|
|
|
exit(0);
|
2015-04-30 08:05:11 +03:00
|
|
|
}
|
2015-05-02 13:45:24 +03:00
|
|
|
|
2015-12-07 19:01:12 +03:00
|
|
|
if (params.GetParam("server")) {
|
|
|
|
std::cerr << "RUN SERVER at pid " << pid << std::endl;
|
2015-05-02 13:45:24 +03:00
|
|
|
return run_as_server();
|
2015-12-09 03:00:35 +03:00
|
|
|
} else
|
2015-05-02 13:45:24 +03:00
|
|
|
return batch_run();
|
|
|
|
}
|
2015-04-29 22:07:03 +03:00
|
|
|
#ifdef NDEBUG
|
2015-05-02 13:45:24 +03:00
|
|
|
catch (const std::exception &e) {
|
|
|
|
std::cerr << "Exception: " << e.what() << std::endl;
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2015-04-29 22:07:03 +03:00
|
|
|
#endif
|
2015-03-04 03:07:11 +03:00
|
|
|
}
|
|
|
|
|