使用模板实现通用字符串解析器

时间:2012-12-11 19:42:59

标签: c++ parsing templates c++11

我正在尝试提出一种解析字符串的通用解决方案(使用给定的格式)。例如,我希望能够解析包含数值列表(整数或浮点数)的字符串并返回一个std :: vector。这就是我到目前为止所做的:

template<typename T, typename U>
T parse_value(const U& u) {
    throw std::runtime_error("no parser available");
}

template<typename T>
std::vector<T> parse_value(const std::string& s) {
    std::vector<std::string> parts;
    boost::split(parts, s, boost::is_any_of(","));
    std::vector<T> res;
    std::transform(parts.begin(), parts.end(), std::back_inserter(res),
            [](const std::string& s) { return boost::lexical_cast<T>(s); });
    return res;
}

此外,我希望能够解析包含其他类型值的字符串。例如:

struct Foo { /* ... */ };

template<>
Foo parse_value(const std::string& s) {
    /* parse string and return a Foo object */
}

维护parse_value函数的单个“层次结构”的原因是,有时,我想使用boost :: optional来解析可选值(可能存在或不存在)。理想情况下,我希望只有一个parse_optional_value函数可以委托相应的parse_value函数:

template<typename T>
boost::optional<T> parse_optional_value(const boost::optional<std::string>& s) {
    if (!s) return boost::optional<T>();
    return boost::optional<T>(parse_value<T>(*s));
}

到目前为止,我当前的解决方案不起作用(编译器无法推断出要使用的确切函数)。我想问题是我的解决方案依赖于根据parse_value函数的返回类型推导出模板值。我不确定如何解决这个问题(甚至是否可以修复它,因为设计方法可能完全有缺陷)。有谁知道解决我想做的事情的方法?如果您能指出一种可能的方法来解决我当前实施中遇到的问题,我将非常感激。顺便说一下,我对解决这个问题的想法完全不同。

2 个答案:

答案 0 :(得分:1)

您不能根据返回值[1]重载函数。这正是标准IO库使用构造的原因:

std::cin >> a >> b;

这可能不是你的小蛋糕 - 许多人不喜欢它,它确实不是没有它的问题 - 但它为解析器提供目标类型做得很好。如上所述,它还具有优于静态parse<X>(const std::string&)原型的优势,它允许链接和流式传输。有时这不是必需的,但在许多解析上下文中它是必不可少的,使用operator>>实际上是一个非常酷的语法。 [2]

标准库不会做最远和最酷的事情,即跳过字符串常量scanf样式并允许交错读取。

vector<int> integers;
std::cin >> "[" >> interleave(integers, ",") >> "]";

然而,可以定义。 (可能最好在字符串文字周围使用一个显式的包装器,但实际上我更喜欢它;但如果你传递一个变量你想要使用包装器。)


[1]使用新的auto声明,其原因变得更加清晰。

另一方面,IO操纵者是一个残酷的玩笑。错误处理是可悲的。但你无法拥有一切。

答案 1 :(得分:0)

以下是libsass解析器的示例:

const char* interpolant(const char* src) {
  return recursive_scopes< exactly<hash_lbrace>, exactly<rbrace> >(src);
}

// Match a single character literal.
// Regex equivalent: /(?:x)/
template <char chr>
const char* exactly(const char* src) {
  return *src == chr ? src + 1 : 0;
}

可以将规则传递给lex方法。