// Make sure boost/asio.hpp is included before windows.h.
@@ -68,6 +67,9 @@
#include "OurWebThreeStubServer.h"
#include "Transact.h"
#include "Debugger.h"
+#include "DappLoader.h"
+#include "DappHost.h"
+#include "WebPage.h"
#include "ui_Main.h"
using namespace std;
using namespace dev;
@@ -116,7 +118,9 @@ Address c_newConfig = Address("c6d9d2cd449a754c494264e1809c50e34d64562b");
Main::Main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Main),
- m_transact(this, this)
+ m_transact(this, this),
+ m_dappLoader(nullptr),
+ m_webPage(nullptr)
{
QtWebEngine::initialize();
setWindowFlags(Qt::Window);
@@ -166,10 +170,12 @@ Main::Main(QWidget *parent) :
m_server->setIdentities(keysAsVector(owned()));
m_server->StartListening();
+ WebPage* webPage= new WebPage(this);
+ m_webPage = webPage;
+ connect(webPage, &WebPage::consoleMessage, [this](QString const& _msg) { Main::addConsoleMessage(_msg, QString()); });
+ ui->webView->setPage(m_webPage);
connect(ui->webView, &QWebEngineView::loadFinished, [this]()
{
-// f->disconnect();
-// f->addToJavaScriptWindowObject("env", this, QWebFrame::QtOwnership);
auto f = ui->webView->page();
f->runJavaScript(contentsOfQResource(":/js/bignumber.min.js"));
f->runJavaScript(contentsOfQResource(":/js/webthree.js"));
@@ -181,6 +187,9 @@ Main::Main(QWidget *parent) :
ui->tabWidget->setTabText(0, ui->webView->title());
});
+ m_dappHost.reset(new DappHost(8081));
+ m_dappLoader = new DappLoader(this, web3());
+ connect(m_dappLoader, &DappLoader::dappReady, this, &Main::dappLoaded);
// ui->webView->page()->settings()->setAttribute(QWebEngineSettings::DeveloperExtrasEnabled, true);
// QWebEngineInspector* inspector = new QWebEngineInspector();
// inspector->setPage(page);
@@ -425,13 +434,7 @@ void Main::eval(QString const& _js)
s = "" + jsonEv.toString().toHtmlEscaped() + "";
else
s = "unknown type";
- m_consoleHistory.push_back(qMakePair(_js, s));
- s = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
- for (auto const& i: m_consoleHistory)
- s += ">" + i.first.toHtmlEscaped() + "
"
- " " + i.second + "
";
- s += "";
- ui->jsConsole->setHtml(s);
+ addConsoleMessage(_js, s);
};
ui->webView->page()->runJavaScript("JSON.stringify(___RET)", f2);
};
@@ -439,6 +442,17 @@ void Main::eval(QString const& _js)
ui->webView->page()->runJavaScript(c, f);
}
+void Main::addConsoleMessage(QString const& _js, QString const& _s)
+{
+ m_consoleHistory.push_back(qMakePair(_js, _s));
+ QString r = "" Div(Mono "position: absolute; bottom: 0; border: 0px; margin: 0px; width: 100%");
+ for (auto const& i: m_consoleHistory)
+ r += ">" + i.first.toHtmlEscaped() + "
"
+ " " + i.second + "
";
+ r += "";
+ ui->jsConsole->setHtml(r);
+}
+
static Public stringToPublic(QString const& _a)
{
string sn = _a.toStdString();
@@ -780,15 +794,28 @@ void Main::on_jitvm_triggered()
void Main::on_urlEdit_returnPressed()
{
QString s = ui->urlEdit->text();
- QRegExp r("([a-z]+://)?([^/]*)(.*)");
- if (r.exactMatch(s))
- if (r.cap(2).isEmpty())
- s = (r.cap(1).isEmpty() ? "file://" : r.cap(1)) + r.cap(3);
+ QUrl url(s);
+ if (url.scheme().isEmpty() || url.scheme() == "eth")
+ {
+ try
+ {
+ //try do resolve dapp url
+ m_dappLoader->loadDapp(s);
+ }
+ catch (...)
+ {
+ qWarning() << boost::current_exception_diagnostic_information().c_str();
+ }
+ }
+
+ if (url.scheme().isEmpty())
+ if (url.path().indexOf('/') < url.path().indexOf('.'))
+ url.setScheme("file");
else
- s = (r.cap(1).isEmpty() ? "http://" : r.cap(1)) + lookup(r.cap(2)) + r.cap(3);
- else{}
- qDebug() << s;
- ui->webView->setUrl(s);
+ url.setScheme("http");
+ else {}
+ qDebug() << url.toString();
+ ui->webView->page()->setUrl(url);
}
void Main::on_nameReg_textChanged()
@@ -973,7 +1000,7 @@ void Main::refreshBlockChain()
// TODO: keep the same thing highlighted.
// TODO: refactor into MVC
// TODO: use get by hash/number
- // TODO: transactions, log addresses, log topics
+ // TODO: transactions
auto const& bc = ethereum()->blockChain();
QStringList filters = ui->blockChainFilter->text().toLower().split(QRegExp("\\s+"), QString::SkipEmptyParts);
@@ -985,15 +1012,17 @@ void Main::refreshBlockChain()
h256 h(f.toStdString());
if (bc.isKnown(h))
blocks.insert(h);
+ for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1))
+ blocks.insert(bc.numberHash(b));
}
else if (f.toLongLong() <= bc.number())
blocks.insert(bc.numberHash(u256(f.toLongLong())));
- /*else if (f.size() == 40)
+ else if (f.size() == 40)
{
- Address h(f[0]);
- if (bc.(h))
- blocks.insert(h);
- }*/
+ Address h(f.toStdString());
+ for (auto const& b: bc.withBlockBloom(LogBloom().shiftBloom<3, 32>(sha3(h)), 0, -1))
+ blocks.insert(bc.numberHash(b));
+ }
QByteArray oldSelected = ui->blocks->count() ? ui->blocks->currentItem()->data(Qt::UserRole).toByteArray() : QByteArray();
ui->blocks->clear();
@@ -1835,3 +1864,9 @@ void Main::refreshWhispers()
ui->whispers->addItem(item);
}
}
+
+void Main::dappLoaded(Dapp& _dapp)
+{
+ QUrl url = m_dappHost->hostDapp(std::move(_dapp));
+ ui->webView->page()->setUrl(url);
+}
diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h
index 378877468..ba89b455a 100644
--- a/alethzero/MainWin.h
+++ b/alethzero/MainWin.h
@@ -54,8 +54,11 @@ namespace jsonrpc {
class HttpServer;
}
-class QQuickView;
+class QWebEnginePage;
class OurWebThreeStubServer;
+class DappLoader;
+class DappHost;
+struct Dapp;
using WatchHandler = std::function;
@@ -99,6 +102,7 @@ public slots:
private slots:
void eval(QString const& _js);
+ void addConsoleMessage(QString const& _js, QString const& _s);
// Application
void on_about_triggered();
@@ -172,6 +176,9 @@ private slots:
void refreshBlockChain();
void addNewId(QString _ids);
+ // Dapps
+ void dappLoaded(Dapp& _dapp); //qt does not support rvalue refs for signals
+
signals:
void poll();
@@ -234,8 +241,6 @@ private:
QString m_privateChain;
dev::Address m_nameReg;
- QNetworkAccessManager m_webCtrl;
-
QList> m_consoleHistory;
QMutex m_logLock;
QString m_logHistory;
@@ -248,4 +253,7 @@ private:
NatspecHandler m_natSpecDB;
Transact m_transact;
+ std::unique_ptr m_dappHost;
+ DappLoader* m_dappLoader;
+ QWebEnginePage* m_webPage;
};
diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp
index df6c5258d..640ffe3d8 100644
--- a/alethzero/Transact.cpp
+++ b/alethzero/Transact.cpp
@@ -175,7 +175,7 @@ void Transact::rejigData()
m_data = fromHex(src);
else if (sourceIsSolidity(src))
{
- dev::solidity::CompilerStack compiler;
+ dev::solidity::CompilerStack compiler(true);
try
{
// compiler.addSources(dev::solidity::StandardSources);
@@ -287,7 +287,7 @@ void Transact::on_send_clicked()
if (sourceIsSolidity(src))
try
{
- dev::solidity::CompilerStack compiler;
+ dev::solidity::CompilerStack compiler(true);
m_data = compiler.compile(src, ui->optimize->isChecked());
for (string const& s: compiler.getContractNames())
{
diff --git a/alethzero/WebPage.cpp b/alethzero/WebPage.cpp
new file mode 100644
index 000000000..08c5c3cc6
--- /dev/null
+++ b/alethzero/WebPage.cpp
@@ -0,0 +1,28 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file WebPage.cpp
+ * @author Arkadiy Paronyan arkadiy@ethdev.com>
+ * @date 2015
+ */
+
+#include "WebPage.h"
+
+void WebPage::javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID)
+{
+ QString prefix = _level == QWebEnginePage::ErrorMessageLevel ? "error" : _level == QWebEnginePage::WarningMessageLevel ? "warning" : "";
+ emit consoleMessage(QString("%1(%2:%3):%4").arg(prefix).arg(_sourceID).arg(_lineNumber).arg(_message));
+}
diff --git a/alethzero/WebPage.h b/alethzero/WebPage.h
new file mode 100644
index 000000000..5f1bb1dc8
--- /dev/null
+++ b/alethzero/WebPage.h
@@ -0,0 +1,40 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see .
+*/
+/** @file WebPage.h
+ * @author Arkadiy Paronyan arkadiy@ethdev.com>
+ * @date 2015
+ */
+
+#pragma once
+
+#include
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic" //QtWebEngineWidgets/qwebenginecertificateerror.h:78:348: error: extra ';'
+#include
+#pragma GCC diagnostic pop
+
+class WebPage: public QWebEnginePage
+{
+ Q_OBJECT
+public:
+ WebPage(QObject* _parent): QWebEnginePage(_parent) { }
+signals:
+ void consoleMessage(QString const& _msg);
+
+protected:
+ void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel _level, const QString& _message, int _lineNumber, const QString& _sourceID) override;
+};
diff --git a/chrono_io/chrono_io b/chrono_io/chrono_io
new file mode 100644
index 000000000..8de9d4f45
--- /dev/null
+++ b/chrono_io/chrono_io
@@ -0,0 +1,1140 @@
+// chrono_io
+//
+// (C) Copyright Howard Hinnant
+// Use, modification and distribution are subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt).
+
+#ifndef _CHRONO_IO
+#define _CHRONO_IO
+
+/*
+
+ chrono_io synopsis
+
+#include
+#include
+
+namespace std
+{
+namespace chrono
+{
+
+template
+class durationpunct
+ : public locale::facet
+{
+public:
+ static locale::id id;
+
+ typedef basic_string string_type;
+ enum {use_long, use_short};
+
+ explicit durationpunct(int use = use_long);
+
+ durationpunct(int use,
+ const string_type& long_seconds, const string_type& long_minutes,
+ const string_type& long_hours, const string_type& short_seconds,
+ const string_type& short_minutes, const string_type& short_hours);
+
+ durationpunct(int use, const durationpunct& d);
+
+ template string_type short_name() const;
+ template string_type long_name() const;
+ template string_type name() const;
+
+ bool is_short_name() const;
+ bool is_long_name() const;
+};
+
+template
+ basic_ostream&
+ duration_short(basic_ostream& os);
+
+template
+ basic_ostream&
+ duration_long(basic_ostream& os);
+
+template
+ basic_ostream&
+ operator<<(basic_ostream& os, const duration& d);
+
+template
+ basic_istream&
+ operator>>(basic_istream& is, duration& d);
+
+template
+ basic_ostream&
+ operator<<(basic_ostream& os,
+ const time_point& tp);
+
+template
+ basic_ostream&
+ operator<<(basic_ostream& os,
+ const time_point& tp);
+
+template
+ basic_ostream&
+ operator<<(basic_ostream& os,
+ const time_point& tp);
+
+template
+ basic_istream&
+ operator>>(basic_istream& is,
+ time_point& tp);
+
+template
+ basic_istream&
+ operator>>(basic_istream& is,
+ time_point& tp);
+
+template
+ basic_istream&
+ operator>>(basic_istream& is,
+ time_point& tp);
+
+template
+class timepunct
+ : public std::locale::facet
+{
+public:
+ typedef std::basic_string string_type;
+
+ static std::locale::id id;
+
+ explicit timepunct(std::size_t refs = 0,
+ const string_type& fmt = string_type(),
+ bool local = false);
+
+ explicit timepunct(std::size_t refs,
+ string_type&& fmt,
+ bool local = false);
+
+ const string_type& fmt() const {return fmt_;}
+ bool local() const {return local_;}
+};
+
+template
+ unspecfiied
+ local_fmt(std::basic_string fmt = std::basic_string());
+
+template
+ unspecfiied
+ local_fmt(const CharT* fmt);
+
+template
+ unspecfiied
+ utc_fmt(std::basic_string fmt = std::basic_string());
+
+template
+ unspecfiied
+ utc_fmt(const CharT* fmt);
+
+} // chrono
+} // std
+
+*/
+
+#include
+#include "ratio_io"
+
+namespace std {
+
+namespace chrono
+{
+
+template
+To
+round(const duration& d)
+{
+ To t0 = duration_cast(d);
+ To t1 = t0;
+ ++t1;
+ typedef typename common_type >::type _D;
+ _D diff0 = d - t0;
+ _D diff1 = t1 - d;
+ if (diff0 == diff1)
+ {
+ if (t0.count() & 1)
+ return t1;
+ return t0;
+ }
+ else if (diff0 < diff1)
+ return t0;
+ return t1;
+}
+
+template
+class durationpunct
+ : public locale::facet
+{
+public:
+ typedef basic_string<_CharT> string_type;
+ enum {use_long, use_short};
+
+private:
+ bool __use_short_;
+ string_type __seconds_;
+ string_type __minutes_;
+ string_type __hours_;
+public:
+ static locale::id id;
+
+ explicit durationpunct(size_t refs = 0,
+ int __use = use_long)
+ : locale::facet(refs), __use_short_(__use) {}
+
+ durationpunct(size_t refs, int __use,
+ string_type __seconds, string_type __minutes,
+ string_type __hours)
+ : locale::facet(refs), __use_short_(__use),
+ __seconds_(std::move(__seconds)),
+ __minutes_(std::move(__minutes)),
+ __hours_(std::move(__hours)) {}
+
+ bool is_short_name() const {return __use_short_;}
+ bool is_long_name() const {return !__use_short_;}
+
+ string_type seconds() const {return __seconds_;}
+ string_type minutes() const {return __minutes_;}
+ string_type hours() const {return __hours_;}
+};
+
+template
+locale::id
+durationpunct<_CharT>::id;
+
+enum {prefix, symbol};
+enum {utc, local};
+
+template
+struct __duration_manip
+{
+ bool __use_short_;
+ basic_string<_CharT> __seconds_;
+ basic_string<_CharT> __minutes_;
+ basic_string<_CharT> __hours_;
+
+ __duration_manip(bool __use_short, basic_string<_CharT> __seconds,
+ basic_string<_CharT> __minutes, basic_string<_CharT> __hours)
+ : __use_short_(__use_short),
+ __seconds_(std::move(__seconds)),
+ __minutes_(std::move(__minutes)),
+ __hours_(std::move(__hours)) {}
+};
+
+class duration_fmt
+{
+ int form_;
+public:
+ explicit duration_fmt(int f) : form_(f) {}
+ // explicit
+ operator int() const {return form_;}
+};
+
+template
+std::basic_ostream&
+operator <<(std::basic_ostream& os, duration_fmt d)
+{
+ os.imbue(std::locale(os.getloc(), new durationpunct(0, d == symbol)));
+ return os;
+}
+
+template
+std::basic_istream&
+operator >>(std::basic_istream& is, duration_fmt d)
+{
+ is.imbue(std::locale(is.getloc(), new durationpunct(0, d == symbol)));
+ return is;
+}
+
+template
+std::basic_ostream&
+operator <<(std::basic_ostream& os, __duration_manip m)
+{
+ os.imbue(std::locale(os.getloc(), new durationpunct(0, m.__use_short_,
+ std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_))));
+ return os;
+}
+
+template
+std::basic_istream&
+operator >>(std::basic_istream& is, __duration_manip m)
+{
+ is.imbue(std::locale(is.getloc(), new durationpunct(0, m.__use_short_,
+ std::move(m.__seconds_), std::move(m.__minutes_), std::move(m.__hours_))));
+ return is;
+}
+
+inline
+__duration_manip
+duration_short()
+{
+ return __duration_manip(durationpunct::use_short, "", "", "");
+}
+
+template
+inline
+__duration_manip<_CharT>
+duration_short(basic_string<_CharT> __seconds,
+ basic_string<_CharT> __minutes,
+ basic_string<_CharT> __hours)
+{
+ return __duration_manip<_CharT>(durationpunct<_CharT>::use_short,
+ std::move(__seconds), std::move(__minutes),
+ std::move(__hours));
+}
+
+inline
+__duration_manip
+duration_long()
+{
+ return __duration_manip(durationpunct::use_long, "", "", "");
+}
+
+template
+inline
+typename enable_if
+<
+ is_convertible<_T2, basic_string<_CharT> >::value &&
+ is_convertible<_T3, basic_string<_CharT> >::value,
+ __duration_manip<_CharT>
+>::type
+duration_long(basic_string<_CharT> __seconds,
+ _T2 __minutes,
+ _T3 __hours)
+{
+ typedef basic_string<_CharT> string_type;
+ return __duration_manip<_CharT>(durationpunct<_CharT>::use_long,
+ std::move(__seconds),
+ string_type(std::move(__minutes)),
+ string_type(std::move(__hours)));
+}
+
+template
+inline
+typename enable_if
+<
+ is_convertible<_T2, basic_string<_CharT> >::value &&
+ is_convertible<_T3, basic_string<_CharT> >::value,
+ __duration_manip<_CharT>
+>::type
+duration_long(const _CharT* __seconds,
+ _T2 __minutes,
+ _T3 __hours)
+{
+ typedef basic_string<_CharT> string_type;
+ return __duration_manip<_CharT>(durationpunct<_CharT>::use_long,
+ string_type(__seconds),
+ string_type(std::move(__minutes)),
+ string_type(std::move(__hours)));
+}
+
+template
+basic_string<_CharT>
+__get_unit(bool __is_long, const basic_string<_CharT>& __seconds,
+ const basic_string<_CharT>&, const basic_string<_CharT>&, _Period)
+{
+ if (__is_long)
+ {
+ if (__seconds.empty())
+ {
+ _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's', 0};
+ return ratio_string<_Period, _CharT>::prefix() + __p;
+ }
+ return ratio_string<_Period, _CharT>::prefix() + __seconds;
+ }
+ if (__seconds.empty())
+ return ratio_string<_Period, _CharT>::symbol() + 's';
+ return ratio_string<_Period, _CharT>::symbol() + __seconds;
+}
+
+template
+inline
+basic_string<_CharT>
+__get_unit(bool __is_long, const basic_string<_CharT>& __seconds,
+ const basic_string<_CharT>&, const basic_string<_CharT>&, ratio<1>)
+{
+ if (__seconds.empty())
+ {
+ if (__is_long)
+ {
+ _CharT __p[] = {'s', 'e', 'c', 'o', 'n', 'd', 's'};
+ return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT));
+ }
+ else
+ return basic_string<_CharT>(1, 's');
+ }
+ return __seconds;
+}
+
+template
+basic_string<_CharT>
+__get_unit(bool __is_long, const basic_string<_CharT>&,
+ const basic_string<_CharT>& __minutes,
+ const basic_string<_CharT>&, ratio<60>)
+{
+ if (__minutes.empty())
+ {
+ if (__is_long)
+ {
+ _CharT __p[] = {'m', 'i', 'n', 'u', 't', 'e', 's'};
+ return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT));
+ }
+ else
+ return basic_string<_CharT>(1, 'm');
+ }
+ return __minutes;
+}
+
+template
+inline
+basic_string<_CharT>
+__get_unit(bool __is_long, const basic_string<_CharT>&,
+ const basic_string<_CharT>&,
+ const basic_string<_CharT>& __hours, ratio<3600>)
+{
+ if (__hours.empty())
+ {
+ if (__is_long)
+ {
+ _CharT __p[] = {'h', 'o', 'u', 'r', 's'};
+ return basic_string<_CharT>(__p, __p + sizeof(__p) / sizeof(_CharT));
+ }
+ else
+ return basic_string<_CharT>(1, 'h');
+ }
+ return __hours;
+}
+
+template
+basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os, const duration<_Rep, _Period>& __d)
+{
+ typename basic_ostream<_CharT, _Traits>::sentry ok(__os);
+ if (ok)
+ {
+ typedef durationpunct<_CharT> _F;
+ typedef basic_string<_CharT> string_type;
+ bool failed = false;
+ try
+ {
+ bool __is_long = true;
+ string_type __seconds;
+ string_type __minutes;
+ string_type __hours;
+ locale __loc = __os.getloc();
+ if (has_facet<_F>(__loc))
+ {
+ const _F& f = use_facet<_F>(__loc);
+ __is_long = f.is_long_name();
+ __seconds = f.seconds();
+ __minutes = f.minutes();
+ __hours = f.hours();
+ }
+ string_type __unit = __get_unit(__is_long, __seconds, __minutes,
+ __hours, typename _Period::type());
+ __os << __d.count() << ' ' << __unit;
+ }
+ catch (...)
+ {
+ failed = true;
+ }
+ if (failed)
+ __os.setstate(ios_base::failbit | ios_base::badbit);
+ }
+ return __os;
+}
+
+template ::value>
+struct __duration_io_intermediate
+{
+ typedef _Rep type;
+};
+
+template
+struct __duration_io_intermediate<_Rep, true>
+{
+ typedef typename conditional
+ <
+ is_floating_point<_Rep>::value,
+ long double,
+ typename conditional
+ <
+ is_signed<_Rep>::value,
+ long long,
+ unsigned long long
+ >::type
+ >::type type;
+};
+
+template
+T
+__gcd(T x, T y)
+{
+ while (y != 0)
+ {
+ T old_x = x;
+ x = y;
+ y = old_x % y;
+ }
+ return x;
+}
+
+template <>
+long double
+inline
+__gcd(long double x, long double y)
+{
+ (void)x;
+ (void)y;
+ return 1;
+}
+
+template
+basic_istream<_CharT, _Traits>&
+operator>>(basic_istream<_CharT, _Traits>& __is, duration<_Rep, _Period>& __d)
+{
+ typedef basic_string<_CharT> string_type;
+ typedef durationpunct<_CharT> _F;
+ typedef typename __duration_io_intermediate<_Rep>::type _IR;
+ _IR __r;
+ // read value into __r
+ __is >> __r;
+ if (__is.good())
+ {
+ // now determine unit
+ typedef istreambuf_iterator<_CharT, _Traits> _I;
+ _I __i(__is);
+ _I __e;
+ if (__i != __e && *__i == ' ') // mandatory ' ' after value
+ {
+ ++__i;
+ if (__i != __e)
+ {
+ string_type __seconds;
+ string_type __minutes;
+ string_type __hours;
+ locale __loc = __is.getloc();
+ if (has_facet<_F>(__loc))
+ {
+ const _F& f = use_facet<_F>(__loc);
+ __seconds = f.seconds();
+ __minutes = f.minutes();
+ __hours = f.hours();
+ }
+ // unit is num / den (yet to be determined)
+ unsigned long long num = 0;
+ unsigned long long den = 0;
+ if (*__i == '[')
+ {
+ // parse [N/D]s or [N/D]seconds format
+ ++__i;
+ _CharT __x;
+ __is >> num >> __x >> den;
+ if (!__is.good() || __x != '/')
+ {
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ __i = _I(__is);
+ if (*__i != ']')
+ {
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ ++__i;
+ const basic_string<_CharT> __units[] =
+ {
+ __get_unit(true, __seconds, __minutes, __hours, ratio<1>()),
+ __get_unit(false, __seconds, __minutes, __hours, ratio<1>())
+ };
+ ios_base::iostate __err = ios_base::goodbit;
+ const basic_string<_CharT>* __k = __scan_keyword(__i, __e,
+ __units, __units + sizeof(__units)/sizeof(__units[0]),
+ use_facet >(__loc),
+ __err);
+ switch ((__k - __units) / 2)
+ {
+ case 0:
+ break;
+ default:
+ __is.setstate(__err);
+ return __is;
+ }
+ }
+ else
+ {
+ // parse SI name, short or long
+ const basic_string<_CharT> __units[] =
+ {
+ __get_unit(true, __seconds, __minutes, __hours, atto()),
+ __get_unit(false, __seconds, __minutes, __hours, atto()),
+ __get_unit(true, __seconds, __minutes, __hours, femto()),
+ __get_unit(false, __seconds, __minutes, __hours, femto()),
+ __get_unit(true, __seconds, __minutes, __hours, pico()),
+ __get_unit(false, __seconds, __minutes, __hours, pico()),
+ __get_unit(true, __seconds, __minutes, __hours, nano()),
+ __get_unit(false, __seconds, __minutes, __hours, nano()),
+ __get_unit(true, __seconds, __minutes, __hours, micro()),
+ __get_unit(false, __seconds, __minutes, __hours, micro()),
+ __get_unit(true, __seconds, __minutes, __hours, milli()),
+ __get_unit(false, __seconds, __minutes, __hours, milli()),
+ __get_unit(true, __seconds, __minutes, __hours, centi()),
+ __get_unit(false, __seconds, __minutes, __hours, centi()),
+ __get_unit(true, __seconds, __minutes, __hours, deci()),
+ __get_unit(false, __seconds, __minutes, __hours, deci()),
+ __get_unit(true, __seconds, __minutes, __hours, deca()),
+ __get_unit(false, __seconds, __minutes, __hours, deca()),
+ __get_unit(true, __seconds, __minutes, __hours, hecto()),
+ __get_unit(false, __seconds, __minutes, __hours, hecto()),
+ __get_unit(true, __seconds, __minutes, __hours, kilo()),
+ __get_unit(false, __seconds, __minutes, __hours, kilo()),
+ __get_unit(true, __seconds, __minutes, __hours, mega()),
+ __get_unit(false, __seconds, __minutes, __hours, mega()),
+ __get_unit(true, __seconds, __minutes, __hours, giga()),
+ __get_unit(false, __seconds, __minutes, __hours, giga()),
+ __get_unit(true, __seconds, __minutes, __hours, tera()),
+ __get_unit(false, __seconds, __minutes, __hours, tera()),
+ __get_unit(true, __seconds, __minutes, __hours, peta()),
+ __get_unit(false, __seconds, __minutes, __hours, peta()),
+ __get_unit(true, __seconds, __minutes, __hours, exa()),
+ __get_unit(false, __seconds, __minutes, __hours, exa()),
+ __get_unit(true, __seconds, __minutes, __hours, ratio<1>()),
+ __get_unit(false, __seconds, __minutes, __hours, ratio<1>()),
+ __get_unit(true, __seconds, __minutes, __hours, ratio<60>()),
+ __get_unit(false, __seconds, __minutes, __hours, ratio<60>()),
+ __get_unit(true, __seconds, __minutes, __hours, ratio<3600>()),
+ __get_unit(false, __seconds, __minutes, __hours, ratio<3600>())
+ };
+ ios_base::iostate __err = ios_base::goodbit;
+ const basic_string<_CharT>* __k = __scan_keyword(__i, __e,
+ __units, __units + sizeof(__units)/sizeof(__units[0]),
+ use_facet >(__loc),
+ __err);
+ switch ((__k - __units) / 2)
+ {
+ case 0:
+ num = 1ULL;
+ den = 1000000000000000000ULL;
+ break;
+ case 1:
+ num = 1ULL;
+ den = 1000000000000000ULL;
+ break;
+ case 2:
+ num = 1ULL;
+ den = 1000000000000ULL;
+ break;
+ case 3:
+ num = 1ULL;
+ den = 1000000000ULL;
+ break;
+ case 4:
+ num = 1ULL;
+ den = 1000000ULL;
+ break;
+ case 5:
+ num = 1ULL;
+ den = 1000ULL;
+ break;
+ case 6:
+ num = 1ULL;
+ den = 100ULL;
+ break;
+ case 7:
+ num = 1ULL;
+ den = 10ULL;
+ break;
+ case 8:
+ num = 10ULL;
+ den = 1ULL;
+ break;
+ case 9:
+ num = 100ULL;
+ den = 1ULL;
+ break;
+ case 10:
+ num = 1000ULL;
+ den = 1ULL;
+ break;
+ case 11:
+ num = 1000000ULL;
+ den = 1ULL;
+ break;
+ case 12:
+ num = 1000000000ULL;
+ den = 1ULL;
+ break;
+ case 13:
+ num = 1000000000000ULL;
+ den = 1ULL;
+ break;
+ case 14:
+ num = 1000000000000000ULL;
+ den = 1ULL;
+ break;
+ case 15:
+ num = 1000000000000000000ULL;
+ den = 1ULL;
+ break;
+ case 16:
+ num = 1;
+ den = 1;
+ break;
+ case 17:
+ num = 60;
+ den = 1;
+ break;
+ case 18:
+ num = 3600;
+ den = 1;
+ break;
+ default:
+ __is.setstate(__err);
+ return __is;
+ }
+ }
+ // unit is num/den
+ // __r should be multiplied by (num/den) / _Period
+ // Reduce (num/den) / _Period to lowest terms
+ unsigned long long __gcd_n1_n2 = __gcd(num, _Period::num);
+ unsigned long long __gcd_d1_d2 = __gcd(den, _Period::den);
+ num /= __gcd_n1_n2;
+ den /= __gcd_d1_d2;
+ unsigned long long __n2 = _Period::num / __gcd_n1_n2;
+ unsigned long long __d2 = _Period::den / __gcd_d1_d2;
+ if (num > numeric_limits::max() / __d2 ||
+ den > numeric_limits::max() / __n2)
+ {
+ // (num/den) / _Period overflows
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ num *= __d2;
+ den *= __n2;
+ // num / den is now factor to multiply by __r
+ typedef typename common_type<_IR, unsigned long long>::type _CT;
+ if (is_integral<_IR>::value)
+ {
+ // Reduce __r * num / den
+ _CT __t = __gcd<_CT>(__r, den);
+ __r /= __t;
+ den /= __t;
+ if (den != 1)
+ {
+ // Conversion to _Period is integral and not exact
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ }
+ if (__r > duration_values<_CT>::max() / num)
+ {
+ // Conversion to _Period overflowed
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ _CT __t = __r * num;
+ __t /= den;
+ if (duration_values<_Rep>::max() < __t)
+ {
+ // Conversion to _Period overflowed
+ __is.setstate(__is.failbit);
+ return __is;
+ }
+ // Success! Store it.
+ __r = _Rep(__t);
+ __d = duration<_Rep, _Period>(__r);
+ }
+ else
+ __is.setstate(__is.failbit | __is.eofbit);
+ }
+ else
+ {
+ if (__i == __e)
+ __is.setstate(__is.eofbit);
+ __is.setstate(__is.failbit);
+ }
+ }
+ else
+ __is.setstate(__is.failbit);
+ return __is;
+}
+
+template
+class timepunct
+ : public std::locale::facet
+{
+public:
+ typedef std::basic_string string_type;
+
+private:
+ string_type fmt_;
+ bool local_;
+
+public:
+ static std::locale::id id;
+
+ explicit timepunct(std::size_t refs = 0,
+ const string_type& fmt = string_type(),
+ bool local = false)
+ : std::locale::facet(refs),
+ fmt_(fmt),
+ local_(local) {}
+
+ explicit timepunct(std::size_t refs,
+ string_type&& fmt,
+ bool local = false)
+ : std::locale::facet(refs),
+ fmt_(std::move(fmt)),
+ local_(local) {}
+
+ const string_type& fmt() const {return fmt_;}
+ bool local() const {return local_;}
+};
+
+template
+std::locale::id
+timepunct::id;
+
+template
+basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os,
+ const time_point& __tp)
+{
+ return __os << __tp.time_since_epoch() << " since boot";
+}
+
+template
+struct __time_manip
+{
+ std::basic_string fmt_;
+ bool local_;
+
+ __time_manip(std::basic_string fmt, bool local)
+ : fmt_(std::move(fmt)),
+ local_(local) {}
+};
+
+template
+std::basic_ostream&
+operator <<(std::basic_ostream& os, __time_manip m)
+{
+ os.imbue(std::locale(os.getloc(), new timepunct(0, std::move(m.fmt_), m.local_)));
+ return os;
+}
+
+template
+std::basic_istream&
+operator >>(std::basic_istream& is, __time_manip m)
+{
+ is.imbue(std::locale(is.getloc(), new timepunct(0, std::move(m.fmt_), m.local_)));
+ return is;
+}
+
+template
+inline
+__time_manip
+local_fmt(std::basic_string fmt)
+{
+ return __time_manip(std::move(fmt), true);
+}
+
+template
+inline
+__time_manip
+local_fmt(const charT* fmt)
+{
+ return __time_manip(fmt, true);
+}
+
+inline
+__time_manip
+local_fmt()
+{
+ return __time_manip("", true);
+}
+
+template
+inline
+__time_manip
+utc_fmt(std::basic_string fmt)
+{
+ return __time_manip(std::move(fmt), false);
+}
+
+template
+inline
+__time_manip
+utc_fmt(const charT* fmt)
+{
+ return __time_manip(fmt, false);
+}
+
+inline
+__time_manip
+utc_fmt()
+{
+ return __time_manip("", false);
+}
+
+class __time_man
+{
+ int form_;
+public:
+ explicit __time_man(int f) : form_(f) {}
+ // explicit
+ operator int() const {return form_;}
+};
+
+template
+std::basic_ostream&
+operator <<(std::basic_ostream& os, __time_man m)
+{
+ os.imbue(std::locale(os.getloc(), new timepunct(0, basic_string(), m == local)));
+ return os;
+}
+
+template
+std::basic_istream&
+operator >>(std::basic_istream& is, __time_man m)
+{
+ is.imbue(std::locale(is.getloc(), new timepunct(0, basic_string(), m == local)));
+ return is;
+}
+
+
+inline
+__time_man
+time_fmt(int f)
+{
+ return __time_man(f);
+}
+
+template
+basic_istream<_CharT, _Traits>&
+operator>>(basic_istream<_CharT, _Traits>& __is,
+ time_point& __tp)
+{
+ _Duration __d;
+ __is >> __d;
+ if (__is.good())
+ {
+ const _CharT __u[] = {' ', 's', 'i', 'n', 'c', 'e', ' ', 'b', 'o', 'o', 't'};
+ const basic_string<_CharT> __units(__u, __u + sizeof(__u)/sizeof(__u[0]));
+ ios_base::iostate __err = ios_base::goodbit;
+ typedef istreambuf_iterator<_CharT, _Traits> _I;
+ _I __i(__is);
+ _I __e;
+ ptrdiff_t __k = __scan_keyword(__i, __e,
+ &__units, &__units + 1,
+ use_facet >(__is.getloc()),
+ __err) - &__units;
+ if (__k == 1)
+ {
+ // failed to read epoch string
+ __is.setstate(__err);
+ return __is;
+ }
+ __tp = time_point(__d);
+ }
+ else
+ __is.setstate(__is.failbit);
+ return __is;
+}
+
+template
+basic_ostream<_CharT, _Traits>&
+operator<<(basic_ostream<_CharT, _Traits>& __os,
+ const time_point& __tp)
+{
+ typename basic_ostream<_CharT, _Traits>::sentry ok(__os);
+ if (ok)
+ {
+ bool failed = false;
+ try
+ {
+ const _CharT* pb = nullptr;
+ const _CharT* pe = pb;
+ bool __local = false;
+ typedef timepunct<_CharT> F;
+ locale loc = __os.getloc();
+ if (has_facet(loc))
+ {
+ const F& f = use_facet(loc);
+ pb = f.fmt().data();
+ pe = pb + f.fmt().size();
+ __local = f.local();
+ }
+ time_t __t = system_clock::to_time_t(__tp);
+ tm __tm;
+ if (__local)
+ {
+ if (localtime_r(&__t, &__tm) == 0)
+ failed = true;
+ }
+ else
+ {
+ if (gmtime_r(&__t, &__tm) == 0)
+ failed = true;
+ }
+ if (!failed)
+ {
+ const time_put<_CharT>& tp = use_facet >(loc);
+ if (pb == pe)
+ {
+ _CharT pattern[] = {'%', 'F', ' ', '%', 'H', ':', '%', 'M', ':'};
+ pb = pattern;
+ pe = pb + sizeof(pattern) / sizeof(_CharT);
+ failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed();
+ if (!failed)
+ {
+ duration __d = __tp - system_clock::from_time_t(__t) +
+ seconds(__tm.tm_sec);
+ if (__d.count() < 10)
+ __os << _CharT('0');
+ ios::fmtflags __flgs = __os.flags();
+ __os.setf(ios::fixed, ios::floatfield);
+ __os << __d.count();
+ __os.flags(__flgs);
+ if (__local)
+ {
+ _CharT sub_pattern[] = {' ', '%', 'z'};
+ pb = sub_pattern;
+ pe = pb + + sizeof(sub_pattern) / sizeof(_CharT);
+ failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed();
+ }
+ else
+ {
+ _CharT sub_pattern[] = {' ', '+', '0', '0', '0', '0', 0};
+ __os << sub_pattern;
+ }
+ }
+ }
+ else
+ failed = tp.put(__os, __os, __os.fill(), &__tm, pb, pe).failed();
+ }
+ }
+ catch (...)
+ {
+ failed = true;
+ }
+ if (failed)
+ __os.setstate(ios_base::failbit | ios_base::badbit);
+ }
+ return __os;
+}
+
+template
+basic_istream<_CharT, _Traits>&
+operator>>(basic_istream<_CharT, _Traits>& __is,
+ time_point& __tp)
+{
+ typename basic_istream<_CharT,_Traits>::sentry ok(__is);
+ if (ok)
+ {
+ ios_base::iostate err = ios_base::goodbit;
+ try
+ {
+ const _CharT* pb = nullptr;
+ const _CharT* pe = pb;
+ bool __local = false;
+ typedef timepunct<_CharT> F;
+ locale loc = __is.getloc();
+ if (has_facet(loc))
+ {
+ const F& f = use_facet(loc);
+ pb = f.fmt().data();
+ pe = pb + f.fmt().size();
+ __local = f.local();
+ }
+ const time_get<_CharT>& tg = use_facet >(loc);
+ tm __tm = {0};
+ if (pb == pe)
+ {
+ _CharT pattern[] = {'%', 'Y', '-', '%', 'm', '-', '%', 'd',
+ ' ', '%', 'H', ':', '%', 'M', ':'};
+ pb = pattern;
+ pe = pb + sizeof(pattern) / sizeof(_CharT);
+ tg.get(__is, 0, __is, err, &__tm, pb, pe);
+ if (err & ios_base::failbit)
+ goto __exit;
+ typedef istreambuf_iterator<_CharT, _Traits> _I;
+ double __sec;
+ _CharT __c = _CharT();
+ __is >> __sec;
+ if (__is.fail())
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ _I __i(__is);
+ _I __eof;
+ __c = *__i;
+ if (++__i == __eof || __c != ' ')
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ __c = *__i;
+ if (++__i == __eof || (__c != '+' && __c != '-'))
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ int __n = __c == '-' ? 1 : -1;
+ __c = *__i;
+ if (++__i == __eof || !isdigit(__c))
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ int __min = (__c - '0') * 600;
+ __c = *__i;
+ if (++__i == __eof || !isdigit(__c))
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ __min += (__c - '0') * 60;
+ __c = *__i;
+ if (++__i == __eof || !isdigit(__c))
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ __min += (__c - '0') * 10;
+ __c = *__i;
+ if (!isdigit(__c))
+ {
+ err |= ios_base::failbit;
+ goto __exit;
+ }
+ ++__i;
+ __min += __c - '0';
+ __min *= __n;
+ time_t __t;
+ __t = timegm(&__tm);
+ __tp = system_clock::from_time_t(__t) + minutes(__min)
+ + round(duration(__sec));
+ }
+ else
+ {
+ tg.get(__is, 0, __is, err, &__tm, pb, pe);
+ }
+ }
+ catch (...)
+ {
+ err |= ios_base::badbit | ios_base::failbit;
+ }
+ __exit:
+ __is.setstate(err);
+ }
+ return __is;
+}
+
+} // chrono
+
+}
+
+#endif // _CHRONO_IO
diff --git a/chrono_io/ratio_io b/chrono_io/ratio_io
new file mode 100644
index 000000000..ade52376c
--- /dev/null
+++ b/chrono_io/ratio_io
@@ -0,0 +1,601 @@
+// ratio_io
+//
+// (C) Copyright Howard Hinnant
+// Use, modification and distribution are subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt).
+
+#ifndef _RATIO_IO
+#define _RATIO_IO
+
+/*
+
+ ratio_io synopsis
+
+#include
+#include
+
+namespace std
+{
+
+template
+struct ratio_string
+{
+ static basic_string symbol();
+ static basic_string prefix();
+};
+
+} // std
+
+*/
+
+#include
+#include
+#include
+
+namespace std {
+
+template
+struct ratio_string
+{
+ static basic_string<_CharT> symbol() {return prefix();}
+ static basic_string<_CharT> prefix();
+};
+
+template
+basic_string<_CharT>
+ratio_string<_Ratio, _CharT>::prefix()
+{
+ basic_ostringstream<_CharT> __os;
+ __os << _CharT('[') << _Ratio::num << _CharT('/')
+ << _Ratio::den << _CharT(']');
+ return __os.str();
+}
+
+// atto
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string(1, 'a');}
+ static string prefix() {return string("atto");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'a');}
+ static u16string prefix() {return u16string(u"atto");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'a');}
+ static u32string prefix() {return u32string(U"atto");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'a');}
+ static wstring prefix() {return wstring(L"atto");}
+};
+
+// femto
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string(1, 'f');}
+ static string prefix() {return string("femto");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'f');}
+ static u16string prefix() {return u16string(u"femto");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'f');}
+ static u32string prefix() {return u32string(U"femto");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'f');}
+ static wstring prefix() {return wstring(L"femto");}
+};
+
+// pico
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string(1, 'p');}
+ static string prefix() {return string("pico");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'p');}
+ static u16string prefix() {return u16string(u"pico");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'p');}
+ static u32string prefix() {return u32string(U"pico");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'p');}
+ static wstring prefix() {return wstring(L"pico");}
+};
+
+// nano
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string(1, 'n');}
+ static string prefix() {return string("nano");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'n');}
+ static u16string prefix() {return u16string(u"nano");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'n');}
+ static u32string prefix() {return u32string(U"nano");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'n');}
+ static wstring prefix() {return wstring(L"nano");}
+};
+
+// micro
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string("\xC2\xB5");}
+ static string prefix() {return string("micro");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'\xB5');}
+ static u16string prefix() {return u16string(u"micro");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'\xB5');}
+ static u32string prefix() {return u32string(U"micro");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'\xB5');}
+ static wstring prefix() {return wstring(L"micro");}
+};
+
+// milli
+
+template <>
+struct ratio_string
+{
+ static string symbol() {return string(1, 'm');}
+ static string prefix() {return string("milli");}
+};
+
+#if HAS_UNICODE_SUPPORT
+
+template <>
+struct ratio_string
+{
+ static u16string symbol() {return u16string(1, u'm');}
+ static u16string prefix() {return u16string(u"milli");}
+};
+
+template <>
+struct ratio_string
+{
+ static u32string symbol() {return u32string(1, U'm');}
+ static u32string prefix() {return u32string(U"milli");}
+};
+
+#endif
+
+template <>
+struct ratio_string
+{
+ static wstring symbol() {return wstring(1, L'm');}
+ static wstring prefix() {return wstring(L"milli");}
+};
+
+// centi
+
+template <>
+struct ratio_string