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

Mat*_*ias 5 evaluation parsing expression variant c++11

我尝试了不同的方法,并在这里询问了有关我的要求的子问题的几个具体问题.但是我的解决方案并没有像预期的那样真正起作用,所以我退后一步,从更广泛的角度来问这里.请记住,我不是C++专业人士.也不是初学者,但我还在学习语言.

所以,我有以下要求.我需要读入包含条件的文本文件,例如"Greater"或"Equals",所有这些函数都返回布尔值.文本文件还包括这些条件的参数.注意,这些参数可以是不同类型(整数,小数等),并且每个这样的条件可以采用不同数量的参数(例如,"等于"需要2个参数,而"之间"将采用3个参数).因此该文件可能如下所示:

Greater, 2, 3
Greater, 2.4, 1.0
Equals, true, true
Between, 20, 10, 30
Run Code Online (Sandbox Code Playgroud)

读入该文件并解析它的逻辑已经完成.现在我需要将所有这些布尔函数与其参数"连接"并检查它们是否都为真.

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

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

seh*_*ehe 6

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

UPDATE

现在添加了谓词函数的调用和实现(GreaterImplEqualsImpl).

我试图聪明地允许混合算术类型之间的比较(但不是例如Greater(bool,string).如果你比较不兼容的类型,你将得到一个std::runtime_error异常,它给调用者提供类型反馈.

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";
    }
}
Run Code Online (Sandbox Code Playgroud)

打印:

--- 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; 
Run Code Online (Sandbox Code Playgroud)