关注点分离。
你的语法变得一团糟,因为你试图将所有逻辑塞到一个地方,但这并不能真正做到这一点。
同时,您已经使生成器成为有状态的,这意味着性能也会下降。
相反,意识到你有一个数学变换(实际值)->元组(度、分、秒、半球)。让我们创建一个小助手来模拟它:
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
现在,您可以制定这样的规则:
karma::rule<iterator_type, LatLongRep()> latitude, longitude;
它们的实现很简单:
latitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< east_west;
Demo
于是整个程序就变成了:
Live On Coliru http://coliru.stacked-crooked.com/a/915456cd73741dd3
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <cmath>
namespace karma = boost::spirit::karma;
typedef std::back_insert_iterator<std::string> iterator_type;
struct LatLongRep {
bool _hemi; double _deg, _min, _sec;
LatLongRep(double val)
: _hemi(0 < val),
_min(60 * std::modf(std::abs(val), &_deg)),
_sec(60 * std::modf(_min, &_min))
{ }
};
BOOST_FUSION_ADAPT_STRUCT(LatLongRep, _deg, _min, _sec, _hemi)
struct genLatLong : karma::grammar<iterator_type, double()> {
genLatLong() : genLatLong::base_type(start)
{
using namespace karma;
east_west.add (true, 'E')(false, 'W');
north_south.add(true, 'N')(false, 'S');
start = latitude;
latitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< east_west;
longitude =
right_align(3, '0') [ uint_ ]
<< right_align(2, '0') [ uint_ ]
<< right_align(5, '0') [ seconds ]
<< north_south;
}
private:
struct secfmt : karma::real_policies<double> {
unsigned precision(double) const { return 2; }
bool trailing_zeros(double) const { return true; }
};
karma::real_generator<double, secfmt> seconds;
karma::symbols<bool, char> east_west, north_south;
karma::rule<iterator_type, double()> start;
karma::rule<iterator_type, LatLongRep()> latitude, longitude;
};
int main()
{
genLatLong const gen;
for(auto & value : std::vector<double>{ 47.25346, 13.984364, -0.1233453, -44.3 })
{
std::string generated;
iterator_type outiter(generated);
auto rv = karma::generate(outiter, gen, value);
std::cout << "(" << std::boolalpha << rv << ") " << value << " ==> " << generated << std::endl;
}
}
Prints
(true) 47.2535 ==> 0471512.46E
(true) 13.9844 ==> 0135903.71E
(true) -0.123345 ==> 0000724.04W
(true) -44.3 ==> 0441760.00W
附加说明/技巧:
使用派生的real_policy
named secfmt
将秒格式化为小数点后两位;看文档 http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/karma/reference/numeric/real_number.html#spirit.karma.reference.numeric.real_number.real_number_formatting_policies
使用融合适应来获得领域LatLongRep
无需过度使用语义操作和/或 Phoenix 绑定(请参阅教程示例 http://www.boost.org/doc/libs/1_59_0/libs/spirit/doc/html/spirit/karma/tutorials/karma_adapted_complex.html#spirit.karma.tutorials.karma_adapted_complex.adapting_a_class_as_a_fusion_sequence)。也可以看看Boost Spirit:“语义行为是邪恶的”? https://stackoverflow.com/questions/8259440/boost-spirit-semantic-actions-are-evil
-
use of karma::symbols<>
设置半球指示器的格式:
karma::symbols<bool, char> east_west, north_south;
east_west.add (true, 'E')(false, 'W');
north_south.add(true, 'N')(false, 'S');
发电机结构现已脱离循环 - 这大大提高了速度
使用所定义的纬度和经度作为读者的练习