gpt4 book ai didi

parsing - 处理框架以从脚本调用不同类型的函数的 C++ 方式是什么?

转载 作者:行者123 更新时间:2023-12-04 17:16:26 25 4
gpt4 key购买 nike

我尝试了不同的方法,并在这里提出了几个关于我的要求的子问题的具体问题。但是我的解决方案并没有真正按预期工作,所以我退后一步,从更一般的角度在这里提问。请记住,我不是 C++ 专家。也不是初学者,但我仍在学习这门语言。

所以,我有以下要求。我需要读入文本文件,其中包含条件,如“Greater”或“Equals”,都类似于返回 bool 值的函数。文本文件还包括这些条件的参数。请注意,这些参数可以是不同的类型(整数、十进制等),并且每个这样的条件可以采用不同数量的参数(例如,“Equals”采用 2 个参数,而“Between”采用 3 个参数)。因此该文件可能如下所示:

Greater, 2, 3
Greater, 2.4, 1.0
Equals, true, true
Between, 20, 10, 30

读入该文件并解析它的逻辑已经完成。现在我需要将所有这些 bool 函数与其参数“连接起来”,并检查它们是否都为真。

所以我想我会创建函数或一个带有静态方法的类来表示这些 bool 测试函数,然后创建一个指向这些函数的函数指针映射,按它们的名称映射。在运行时,我会读入文件,调用相应的函数指针并传入参数。这对我来说似乎很容易,但实际上我主要是因为这些 bool 函数可以采用不同数量的参数,并且这些参数可以是不同的类型这一事实而苦苦挣扎。

你能推荐一种方法来解决 C++ 中的这个要求吗?我不是要一个完整的解决方案,而是要一个合适的 C++ 方法,或者我可以遵循的指南。提前致谢!

最佳答案

这是显示的输入的快速而肮脏的 Spirit 语法。

UPDATE

Now added invocation and implementation of the predicate functions (GreaterImpl and EqualsImpl).

I tried to be smart allowing comparisons between mixed arithmetic types (but not e.g. Greater(bool,string). If you compare incompatible types you will get a std::runtime_error exception that provides type feedback to the caller.


Live On Coliru
#include <deque>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted/struct.hpp>

namespace qi = boost::spirit::qi;

namespace Ast {
using Value = boost::variant<int, double, bool, std::string>;

using BinaryPred = std::function<bool(Value, Value)>;
using TernaryPred = std::function<bool(Value, Value, Value)>;
using Pred = boost::variant<BinaryPred, TernaryPred>;
using Values = std::vector<Value>;

struct Invocation { Pred pred; Values args; };
using Invocations = std::vector<Invocation>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Invocation, pred, args)

namespace Predicates {
using Ast::Value;

struct Greater : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}

template <typename T> bool operator()(T const& a, T const& b) const { return std::greater<T>{}(a, b); }

template <typename T, typename U>
typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>(), bool>::type
operator()(T const& a, U const& b) const { return a > b; }

template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};

struct Equals : boost::static_visitor<bool> {
bool operator()(Value const& a, Value const& b) const {
return boost::apply_visitor(*this, a, b);
}

template <typename T> bool operator()(T const& a, T const& b) const { return std::equal_to<T>{}(a, b); }

template <typename T, typename U, typename enable = typename std::enable_if<std::is_arithmetic<T>() && std::is_arithmetic<U>()>::type >
bool operator()(T const& a, U const& b) const { return a == b; }

template <typename T, typename U>
typename std::enable_if<not (std::is_arithmetic<T>() && std::is_arithmetic<U>()), bool>::type
operator()(T const&, U const&) const { throw std::runtime_error("Type Mismatch"); }
};

struct Between {
bool operator()(Value const& v, Value const& lower, Value const& upper) const {
return Greater{}(v,lower) && Greater{}(upper,v);
}
};

}

static inline bool evaluate(Ast::Invocation const& i) {
struct Invoker {
using result_type = bool;
Ast::Values const& args;

result_type operator()(Ast::BinaryPred const& p) const {
if (args.size() != 2) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1));
}
result_type operator()(Ast::TernaryPred const& p) const {
if (args.size() != 3) throw std::runtime_error("Arity Mismatch");
return p(args.at(0), args.at(1), args.at(2));
}
};

return boost::apply_visitor(Invoker{i.args}, i.pred);
}

template <typename It>
struct Grammar : qi::grammar<It, Ast::Invocations()> {

Grammar() : Grammar::base_type(start) {
using namespace qi;

start = skip(blank) [ invocation % eol ];
invocation = pred >> -("," >> args);
args = arg % ",";
arg = my_double_ | qi::int_ | qi::bool_ | lexeme['"' > *~char_('"') > '"'];
}

private:
struct pred_t : qi::symbols<char, Ast::Pred> {
pred_t() {
this->add
("Greater", Predicates::Greater{})
("Equals", Predicates::Equals{})
("Between", Predicates::Between{})
;
}
} const pred;
qi::rule<It, Ast::Invocations()> start;
qi::rule<It, Ast::Invocation(), qi::blank_type> invocation;
qi::rule<It, Ast::Values(), qi::blank_type> args;
qi::rule<It, Ast::Value(), qi::blank_type> arg;
qi::real_parser<double, qi::strict_real_policies<double> > my_double_;
};

#include <sstream>

int main() {
using It = boost::spirit::istream_iterator;

std::deque<std::string> testcases {
// one multiline case:
"Between, 20, 10, 30\n"
"Between, NaN, NaN, NaN\n"
"Between, \"q\", \"a\", \"z\""
};

// many single line cases for easy test reporting
for (std::string op : {"Greater","Equals"})
for (auto rhs : { "42", "0.0", "true", "\"hello\"" })
for (auto lhs : { "41", "-0.0", "false", "\"bye\"" }) {
testcases.push_front(op + ", " + lhs + ", " + rhs);
}

for (auto testcase : testcases) {
std::cout << "--- Testcase '" << testcase << "' -> ";

std::istringstream iss(testcase);
It f(iss >> std::noskipws), l;
Ast::Invocations parsed;

if (qi::parse(f, l, Grammar<It>(), parsed)) {
for (auto& invocation : parsed) {
try {
std::cout << std::boolalpha << evaluate(invocation) << "; ";
} catch(std::exception const& e) {
std::cout << e.what() << "; ";
}
}
std::cout << "\n";
} else {
std::cout << "Parse failed\n";
}

if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
}
打印:
--- Testcase 'Equals, "bye", "hello"' -> false; 
--- Testcase 'Equals, false, "hello"' -> Type Mismatch;
--- Testcase 'Equals, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Equals, 41, "hello"' -> Type Mismatch;
--- Testcase 'Equals, "bye", true' -> Type Mismatch;
--- Testcase 'Equals, false, true' -> false;
--- Testcase 'Equals, -0.0, true' -> false;
--- Testcase 'Equals, 41, true' -> false;
--- Testcase 'Equals, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Equals, false, 0.0' -> true;
--- Testcase 'Equals, -0.0, 0.0' -> true;
--- Testcase 'Equals, 41, 0.0' -> false;
--- Testcase 'Equals, "bye", 42' -> Type Mismatch;
--- Testcase 'Equals, false, 42' -> false;
--- Testcase 'Equals, -0.0, 42' -> false;
--- Testcase 'Equals, 41, 42' -> false;
--- Testcase 'Greater, "bye", "hello"' -> false;
--- Testcase 'Greater, false, "hello"' -> Type Mismatch;
--- Testcase 'Greater, -0.0, "hello"' -> Type Mismatch;
--- Testcase 'Greater, 41, "hello"' -> Type Mismatch;
--- Testcase 'Greater, "bye", true' -> Type Mismatch;
--- Testcase 'Greater, false, true' -> false;
--- Testcase 'Greater, -0.0, true' -> false;
--- Testcase 'Greater, 41, true' -> true;
--- Testcase 'Greater, "bye", 0.0' -> Type Mismatch;
--- Testcase 'Greater, false, 0.0' -> false;
--- Testcase 'Greater, -0.0, 0.0' -> false;
--- Testcase 'Greater, 41, 0.0' -> true;
--- Testcase 'Greater, "bye", 42' -> Type Mismatch;
--- Testcase 'Greater, false, 42' -> false;
--- Testcase 'Greater, -0.0, 42' -> false;
--- Testcase 'Greater, 41, 42' -> false;
--- Testcase 'Between, 20, 10, 30
Between, NaN, NaN, NaN
Between, "q", "a", "z"' -> true; false; true;

关于parsing - 处理框架以从脚本调用不同类型的函数的 C++ 方式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41187486/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com