1

関数「TakeOne」のパーサー コードを以下に示します。TakeOne 関数は、'%null%' と等しくない最初のパラメーターを返すように機能します。例:

TakeOne( %null% , '3', 'defaultVal'); -->  result = 3
TakeOne( 5 , 'asd', 'defaultVal');   -> result = 5

今、私はこの関数を

TakeOne(parm1, parm2, ... , defaultValue);

C++11 の機能を使用せずにこれを行うことは可能ですか? ありがとう

#include <string>
#include <fstream>
#include <algorithm>
#include "sstream"
#include <locale.h>
#include <iomanip>

#define BOOST_SPIRIT_USE_PHOENIX_V3

#include <boost/functional/hash.hpp>
#include <boost/variant.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/tokenizer.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/phoenix/function/adapt_function.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/round.hpp>
#include <boost/exception/diagnostic_information.hpp> 
#include <boost/algorithm/string.hpp> 

namespace qi    = boost::spirit::qi;
namespace phx   = boost::phoenix;

typedef double NumValue;
typedef boost::variant<double, std::wstring> GenericValue;

const std::wstring ParserNullChar = L"%null%";
const double NumValueDoubleNull = std::numeric_limits<double>::infinity();

//Convert string to numeric values
struct AsNumValue : boost::static_visitor<double>
{
    double operator()(double d)              const { return d; }
    double operator()(std::wstring const& s) const
    { 
        if(boost::iequals(s, ParserNullChar))
        {
            return NumValueDoubleNull;
        }
        try { return boost::lexical_cast<double>(s); } 
        catch(...) 
        {
            throw;
        }
    }
};

double Num(GenericValue const& val)
{ 
    return boost::apply_visitor(AsNumValue(), val);
}

bool CheckIfNumValueIsNull(double num)
{
    if(num == NumValueDoubleNull)
        return true;
    else
        return false;
}


bool CheckIfGenericValIsNull(const GenericValue& val)
{
    std::wostringstream woss;
    woss << val;

    if(boost::iequals(woss.str(), ParserNullChar))
    {
        return true;
    }
    else if(val.which() != 1 && CheckIfNumValueIsNull(Num(val)))
    {
        return true;
    }
    else
        return false;
}


GenericValue TakeOne(GenericValue val1, GenericValue val2, GenericValue def)
{
  if(!CheckIfGenericValIsNull(val1))
    {
         return val1;
    }
    else if(!CheckIfGenericValIsNull(val2))
    {
        return val2;
    }
    else
        return def;
}



BOOST_PHOENIX_ADAPT_FUNCTION(GenericValue, TakeOne_, TakeOne, 3)

template <typename It, typename Skipper = qi::space_type >
struct MapFunctionParser : qi::grammar<It, GenericValue(), Skipper>
{
    MapFunctionParser() : MapFunctionParser::base_type(expr_)
    {
        using namespace qi;

        function_call_ = 
          (no_case[L"TakeOne"] > '(' > expr_ > ',' > expr_ > ',' > expr_ > ')') 
            [_val = TakeOne_(_1, _2, _3) ];


        string_ =   (L'"' > *~char_('"') > L'"')
            | (L"'" > *~char_("'") > L"'"); 


        factor_ =
    (no_case[ParserNullChar])     [_val = NumValueDoubleNull]
        |   double_                    [ _val = _1]
        |   string_              [ _val = _1]
        |   function_call_       [ _val = _1]
        ;


        expr_ = factor_;

        on_error<fail> ( expr_, std::cout
            << phx::val("Error! Expecting ") << _4 << phx::val(" here: \"")
            << phx::construct<std::string>(_3, _2) << phx::val("\"\n"));

#ifdef _DEBUG 
        BOOST_SPIRIT_DEBUG_NODE(function_call_);
        BOOST_SPIRIT_DEBUG_NODE(expr_);
        BOOST_SPIRIT_DEBUG_NODE(string_);
        BOOST_SPIRIT_DEBUG_NODE(factor_);
#endif
    }

private:
    qi::rule<It, std::wstring()> string_;
    qi::rule<It, GenericValue(), Skipper> function_call_, expr_, factor_;

};


int main()
{
    std::wstringstream wss;
    typedef std::wstring::const_iterator AttIter;
    MapFunctionParser<AttIter , boost::spirit::qi::space_type> mapFunctionParser;
    bool ret;
    GenericValue result;

    std::wstring functionStr = L"TakeOne(%null%, 5, 'default')";
    std::wstring::const_iterator beginExpression(functionStr.begin());
    std::wstring::const_iterator endExpression(functionStr.end());

    ret = boost::spirit::qi::phrase_parse(beginExpression,endExpression,mapFunctionParser,boost::spirit::qi::space,result);

    std::wcout << result << std::endl;
    return 0;
}
4

2 に答える 2

0

更新: 2 番目の回答で他の懸念事項に対処しました。コードは77 行になり、よりシンプルで堅牢になりました (そして質問にも答えています)。

フェニックスの質問に対する直接の答えは「はい」です。

struct TakeOne
{
    template <typename...> struct result { typedef GenericValue type; };

    template <typename... Args> 
        GenericValue operator()(Args const&... args) const {
            return first(args...);
        }

private:
    GenericValue first(GenericValue const& def) const {
        return def;
    }
    template <typename... Args> GenericValue first(GenericValue const& def, GenericValue const& head, Args const&... tail) const {
        if (CheckIfGenericValIsNull(head)) 
            return first(def, tail...);
        else 
            return head;
    }
};

static const boost::phoenix::function<TakeOne> TakeOne_;

そして、ほとんど同じように動作します (ただし、最初の引数としてデフォルトを渡す必要があります):

function_call_ = no_case[L"TakeOne"] > (
        ('(' > expr_ > ',' > expr_ > ')'                      ) [_val=TakeOne_(_2,_1)]
      | ('(' > expr_ > ',' > expr_ > ',' > expr_ > ')'        ) [_val=TakeOne_(_3,_1,_2)]
      | ('(' > expr_ > ',' > expr_ > ',' > expr_ > expr_ > ')') [_val=TakeOne_(_4,_1,_2,_3)]
      // ... etc
      );

ただし、ご覧のとおり、それほど柔軟ではありません。可変個引数は静的に既知の数の引数を意味するため、可変個引数はおそらくあなたが望んでいたものではありません。ランタイム入力 (解析中) に依存するものは、「静的に既知」のカテゴリに収まることはありません。だから私はこれを提案します:

    function_call_ = 
        no_case[L"TakeOne"] > 
        ('(' > expr_ % ',' > ')') [_val=TakeOne_(_1)];

したがって、代わりに a を渡していますstd::vector<GenericValue>。今、TakeOneそよ風になります:

GenericValue TakeOne(std::vector<GenericValue> const& arglist) {
    assert(!arglist.empty());
    for (auto& v : arglist)
        if (!CheckIfGenericValIsNull(v))
            return v;
    return arglist.back(); // last is the default
}
BOOST_PHOENIX_ADAPT_FUNCTION(GenericValue, TakeOne_, TakeOne, 1)

ボーナス:

簡単にするために、ここで再考された訪問者は次のとおりです。

struct IsNull : boost::static_visitor<bool> {
    bool operator()(double num) const { 
        return (num == NumValueDoubleNull);
    }
    bool operator()(std::wstring const& s) const { 
        return boost::iequals(s, ParserNullChar);
    }
};

GenericValue TakeOne(std::vector<GenericValue> const& arglist) {
    assert(!arglist.empty());
    for (auto& v : arglist)
        if (!boost::apply_visitor(IsNull(), v))
            return v;
    return arglist.back(); // last is the default
}
BOOST_PHOENIX_ADAPT_FUNCTION(GenericValue, TakeOne_, TakeOne, 1)

その「修正」だけで、最大 51 の LoC (112 対 163) を節約できますColiruでライブを見る

于 2013-11-05T15:47:34.850 に答える