日時のフォーマッター
日時を文字列化するプログラムを書きました。datetime_string("%Y/%m/%d")
のような記法だとローカライズで困るので、名前付き引数で順不同に要素を指定させるようにしました。
#include <cstdint> #include <string> #include <boost/format.hpp> #include <boost/optional.hpp> #include <boost/parameter/name.hpp> namespace param { BOOST_PARAMETER_NAME(years) BOOST_PARAMETER_NAME(months) BOOST_PARAMETER_NAME(days) BOOST_PARAMETER_NAME(hours) BOOST_PARAMETER_NAME(minutes) BOOST_PARAMETER_NAME(seconds) } class default_formatter { public: std::string date(std::uint32_t years, std::uint32_t months, std::uint32_t days) const { return (boost::format("%1%/%2%/%3%") % years % months % days).str(); } std::string hour_minute(std::uint32_t hours, std::uint32_t minutes) const { return (boost::format("%1%:%2%") % hours % minutes).str(); } std::string hour_minute_second(std::uint32_t hours, std::uint32_t minutes, std::uint32_t seconds) const { return (boost::format("%1%:%2%:%3%") % hours % minutes % seconds).str(); } std::string datetime(std::uint32_t years, std::uint32_t months, std::uint32_t days, std::uint32_t hours, std::uint32_t minutes) const { return (boost::format("%1%/%2%/%3% %4%:%5%") % years % months % days % hours % minutes ).str(); } std::string full_datetime(std::uint32_t years, std::uint32_t months, std::uint32_t days, std::uint32_t hours, std::uint32_t minutes, std::uint32_t seconds) const { return (boost::format("%1%/%2%/%3% %4%:%5%:%6%") % years % months % days % hours % minutes % seconds ).str(); } }; class datetime_string_maker { public: using value_type = std::uint32_t; template <class Formatter, class ArgumentPack> std::string make(const Formatter& formatter, const ArgumentPack& args) const { using opt_value = boost::optional<value_type>; const opt_value none_opt = opt_value(); opt_value years = args[param::_years | none_opt]; opt_value months = args[param::_months | none_opt]; opt_value days = args[param::_days | none_opt]; opt_value hours = args[param::_hours | none_opt]; opt_value minutes = args[param::_minutes | none_opt]; opt_value seconds = args[param::_seconds | none_opt]; const bool has_date = years && months && days; const bool has_time = hours && minutes; const bool has_fulltime = has_time && seconds; if (!has_date && !has_time) throw std::invalid_argument("invalid argument"); if (has_date && !has_time) return formatter.date(years.get(), months.get(), days.get()); if (has_time && !has_fulltime && !has_date) return formatter.hour_minute(hours.get(), minutes.get()); if (has_fulltime && !has_date) return formatter.hour_minute_second(hours.get(), minutes.get(), seconds.get()); if (has_date && has_time && !has_fulltime) return formatter.datetime( years.get(), months.get(), days.get(), hours.get(), minutes.get()); if (has_date && has_fulltime) return formatter.full_datetime( years.get(), months.get(), days.get(), hours.get(), minutes.get(), seconds.get()); throw std::runtime_error("non reachable path"); } }; template <class ArgumentPack> std::string datetime_string(const ArgumentPack& args) { return datetime_string_maker().make(default_formatter(), args); } #include <iostream> int main() { using namespace param; std::cout << datetime_string((_years = 1985, _months = 3, _days = 1)) << std::endl; std::cout << datetime_string((_hours = 15, _minutes = 30)) << std::endl; std::cout << datetime_string((_years = 1985, _months = 3, _days = 1, _hours = 15, _minutes = 30)) << std::endl; }
出力:
1985/3/1 15:30 1985/3/1 15:30
設計として、名前付き引数の解析とフォーマットを分けています。ローカライズの際には、フォーマット設定の部分だけ条件分岐しましょう。