忍者ブログ
ゲーム関連の話題に絞っていましたが、全然書かないのでいろいろ解禁してみたり。
[1]  [2]  [3]  [4
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

今回は組み込み関数に対応する引数に応じて後々のルールを変更する方法と言うことでひとつ。

#include <iostream>
#include <string>

#pragma warning(push)
#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。
#pragma warning(disable:4819) //ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。
#pragma warning(disable:4127) //条件式が定数です。

#include <boost/spirit/include/qi_action.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi_lexeme.hpp>
#include <boost/spirit/include/qi_operator.hpp>
#include <boost/spirit/include/qi_parse.hpp>
#include <boost/spirit/include/qi_rule.hpp>

#include <boost/spirit/include/qi_char_.hpp>
#include <boost/spirit/include/qi_string.hpp>
#include <boost/spirit/include/qi_numeric.hpp>

#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_statement.hpp>

#pragma warning(pop)

#pragma warning(disable:4503) //装飾された名前の長さが限界を越えました。

template<typename String, typename Skipper>
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, Skipper>{
    typedef typename String::const_iterator Iterator;
   
    boost::spirit::qi::rule<Iterator, Skipper> func1;
    boost::spirit::qi::rule<Iterator, Skipper> func2;
    boost::spirit::qi::symbols<char, boost::spirit::qi::rule<Iterator, Skipper>> funcs;
   
    boost::spirit::qi::rule<Iterator, Skipper> statement;
    boost::spirit::qi::rule<Iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<Iterator, Skipper>>, Skipper> statement2;

    Parser() : Parser::base_type(statement){
        //文法定義
        {
            using namespace boost::spirit;
            using namespace boost::spirit::ascii;
            using namespace boost::spirit::labels;
            namespace phoenix = boost::phoenix;

            //テストコード

            func1 = '(' >> int_ >> ')';
            func2 = '(' >> char_ >> "," >> char_ >> ')';
            funcs.add
                ("func1", func1)
                ("func2", func2)
                ;
            statement2 = (funcs[_a = _1] >> lazy(_a));
            statement = statement2;
        }
    }
};

template<typename String>
struct SkipParser : boost::spirit::qi::grammar<typename String::const_iterator>{
    typedef typename String::const_iterator Iterator;

    SkipParser() : SkipParser::base_type(comment){
        using namespace boost::spirit;

        comment  = boost::spirit::ascii::space
            | ("//" >> *(qi::char_ - '\n') >> '\n')
            | ("/*" >> *(qi::char_ - "*/") >> "*/");
    }

    boost::spirit::qi::rule<Iterator> comment;
};

int main(void)
{
    namespace phoenix = boost::phoenix;

    typedef SkipParser<std::string> SkipperType;
    typedef Parser<std::string, SkipperType> ParserType;
    SkipperType skipper;

    ParserType parser;

    std::string str;
    while (getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        std::string::const_iterator it = str.begin();
        std::string::const_iterator end = str.end();

        bool result = phrase_parse(it, end, parser, skipper);

        if (result && it == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << str << " Parses OK: " << std::endl;
        }
        else if(result)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << std::string(str.begin(), it) << " Parses OK: " << std::endl;
            std::cout << std::string(it, end) << " amari" << std::endl;
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
        }
    }

    return 0;
}

symbolsさんを使います。
boost::spirit::qi::symbols<char, boost::spirit::qi::rule<Iterator, Skipper>> funcs;
コレは、前者の値に対応して後者を返すルールを簡単に作れるもの……です。多分。
今回は関数名と対応するルールで作りましたが、もっとわかりやすい例としては
boost::spirit::qi::symbols<char, int> nums;

nums.add
    ("one", 1)
    ("two", 2)
    ("three", 3)
    ;

とかして英語と数字のマッピングなんかも出来ちゃいます。多分。

あと、この部分
func1 = '(' >> int_ >> ')';
func2 = '(' >> char_ >> "," >> char_ >> ')';
funcs.add
    ("func1", func1)
    ("func2", func2)
    ;
statement2 = (funcs[_a = _1] >> lazy(_a));
statement = statement2;
を、こんな感じ
statement = statement2;
statement2 = (funcs[_a = _1] >> lazy(_a));
funcs.add
    ("func1", func1)
    ("func2", func2)
    ;

func1 = '(' >> int_ >> ')';
func2 = '(' >> char_ >> "," >> char_ >> ')';

にすると、うまく動きません。実行時エラー起こします。
設定の順番が大事みたいです。

さらに、この部分
statement2 = (funcs[_a = _1] >> lazy(_a));
statement = statement2;

を、こんな感じ
statement = (funcs[_a = _1] >> lazy(_a));

にしたい場合、
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<typename String::const_iterator, Skipper>>, Skipper>{

    <中略>

    boost::spirit::qi::rule<Iterator, boost::spirit::qi::locals<boost::spirit::qi::rule<Iterator, Skipper>>, Skipper> statement;

ってしないとダメみたいです。

とりあえず今日はこんなところで。
PR
今回は、定義されていない変数があった場合、セマンティックアクションから構文解析を失敗させる方法です。

なお、以下のコードはboostの1.44.0とVC++2010 Expressで動作確認しています。
#include <iostream>
#include <string>
#include <map>

#pragma warning(push)
#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。
#pragma warning(disable:4819) //ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。
#pragma warning(disable:4127) //条件式が定数です。

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/foreach.hpp>

#pragma warning(pop)

#pragma warning(disable:4503) //装飾された名前の長さが限界を越えました。

template<typename String, typename Skipper>
struct Parser : boost::spirit::qi::grammar<typename String::const_iterator, Skipper>{
    typedef typename String::const_iterator Iterator;

    struct Callback{
        boost::function<void (const String&, int)> SetValiable;    //変数名と値を渡して、変数を保持する関数
        boost::function<int (const String&)> GetValiable;        //変数名を渡して、変数を返す関数

        Callback(boost::function<void (const String&, int)> SetValiable,
            boost::function<int (const String&)> GetValiable) :
            SetValiable(SetValiable),
            GetValiable(GetValiable){}

        Callback(){}
    };

    Parser(const Callback& callback) : Parser::base_type(line){
        //その他初期化
        if(!SetCallback(callback)) throw std::invalid_argument("callback");

        //文法定義
        {
            using namespace boost::spirit;
            using namespace boost::spirit::ascii;

            //式
            //expression_group = '(' >> expression[_val = _1] >> ')';
            expression_group = '(' >> expression >> ')';
            expression_factor = int_  //数字
                | expression_group//部分式の演算結果
                | variable_reference;    //変数

            expression_term    = expression_factor[_val = _1] >> *(
                ('*' >> expression_factor[_val *= _1])
                | ('/' >> expression_factor[_val /= _1])
                );
            expression    = expression_term[_val = _1] >> *(
                ('+' >> expression_term[_val += _1])
                | ('-' >> expression_term[_val -= _1])
                );
            //変数
            variable = lexeme[
                    //alpha[_val += _1] >> +alnum[_val += _1]
                    alpha >> +alnum
                ];
            variable_reference    = variable[
                boost::phoenix::try_[
                    _val = boost::phoenix::bind(&Parser::GetVariableValue, this, _1)
                ].catch_all[
                    _pass = false
                ]];
            variable_assignment    = ( variable >> "=" >> expression )[boost::phoenix::bind(&Parser::SetVariableValue, this, _1, _2)];

            line = variable_assignment;
        }
    }

private:
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_term;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_factor;
    boost::spirit::qi::rule<Iterator, int(), Skipper> expression_group;

    boost::spirit::qi::rule<Iterator, String(), Skipper> variable;
    boost::spirit::qi::rule<Iterator, int(), Skipper> variable_reference;
    boost::spirit::qi::rule<Iterator, Skipper> variable_assignment;

    boost::spirit::qi::rule<Iterator, Skipper> line;

    bool SetCallback(const Callback& callback){
        if(callback.SetValiable
            && callback.GetValiable){
                this->callback = callback;
                return true;
        }

        return false;
    }

    int GetVariableValue(String name){
        return callback.GetValiable(name);
    }
    void SetVariableValue(String name, int i){
        callback.SetValiable(name, i);
    }

    void CallFunction(String name, String arg){
        callback.CallFunction(name, arg);
    }

    Callback callback;
};

template<typename String>
struct SkipParser : boost::spirit::qi::grammar<typename String::const_iterator>{
    typedef typename String::const_iterator Iterator;

    SkipParser() : SkipParser::base_type(comment){
        using namespace boost::spirit;
        //using namespace boost::spirit::arg_names;

        comment  = boost::spirit::ascii::space
            | ("//" >> *(qi::char_ - '\n') >> '\n')
            | ("/*" >> *(qi::char_ - "*/") >> "*/");
    }

    boost::spirit::qi::rule<Iterator> comment;
};

class VariableManager{
public:
    void Set(std::string name, int value){
        container[name] = value;
    }

    int Get(std::string name){
        auto it = container.find(name);
        if(it == container.end())    throw std::exception();

        return it->second;
    }

    void View(){
        BOOST_FOREACH(auto pair, container){
            std::cout << pair.first << ":" << pair.second << std::endl;
        }
    }

private:
    typedef std::map<std::string, int> Container;
    Container container;
};

int main(void)
{
    namespace phoenix = boost::phoenix;

    typedef SkipParser<std::string> SkipperType;
    typedef Parser<std::string, SkipperType> ParserType;
    SkipperType skipper;

    VariableManager vm;

    ParserType::Callback callback(phoenix::bind(&VariableManager::Set, &vm, phoenix::arg_names::arg1, phoenix::arg_names::arg2),
        phoenix::bind(&VariableManager::Get, &vm, phoenix::arg_names::arg1));

    ParserType parser(callback);

    std::string str;
    while (getline(std::cin, str))
    {
        if (str.empty() || str[0] == 'q' || str[0] == 'Q')
            break;

        std::string::const_iterator it = str.begin();
        std::string::const_iterator end = str.end();

        bool result = phrase_parse(it, end, parser, skipper);

        if (result && it == end)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << str << " Parses OK: " << std::endl;
            vm.View();
        }
        else if(result)
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << std::string(str.begin(), it) << " Parses OK: " << std::endl;
            std::cout << std::string(it, end) << " amari" << std::endl;
            vm.View();
        }
        else
        {
            std::cout << "-------------------------\n";
            std::cout << "Parsing failed\n";
            std::cout << "-------------------------\n";
            vm.View();
        }
    }

    return 0;
}
前のコードから変数管理部分を外だしにしたり、ちょっとずつ違いますけど……

ポイントはここ。
variable_reference    = variable[
boost::phoenix::try_[
    _val = boost::phoenix::bind(&Parser::GetVariableValue, this, _1)
].catch_all[
    _pass = false
]];
こっそりphoenixの例外処理とか使ってますが。
_pass(boost::sprit::_pass)にfalseを代入すると構文解析に失敗してくれます。
ただし、バックトラックとかしてくれる関係で、変数が一つも存在しないときに
hoge = 3 + fuga
を解析するとhoge = 3までは解析成功してしまいます。

後、本筋とは関係ないのですけど、以前まで
//expression_group = '(' >> expression[_val = _1] >> ')';
とかしていた部分は単純に
expression_group = '(' >> expression >> ')';
でよきに計らってくれるみたいです。

スマートポインタはいろいろと便利ですが、そのままでは各種ハンドルの保持が出来ません。
大体ハンドルは何かのポインタなので、shared_ptr<void>で保持するのも一つの手ですが、
unique_ptrで十分な箇所ではなんだかもったいない感じです。

かといって、unique_ptrだと関数オブジェクトを作ったりテンプレート引数にいろいろ渡したり不便です。
なので、簡単に利用できるようにテンプレート化してみました。
多分問題なく使用出来るはず……です。
無保証って事で一つ。
適当にコピーとかして使ってください。

#include<functional>
#include<memory>
#include<iostream>

template<typename T>
struct Deleter{
    typedef T pointer;
    typedef std::function<void(T)> DeleteFunction;

    DeleteFunction df;

    void operator()(T rc){
        df(rc);
    }

    Deleter(DeleteFunction df){
        this->df = df;
    }
};

template<typename T>
std::unique_ptr<T, Deleter<T>> CreateUniquePtr(T t, typename Deleter<T>::DeleteFunction df){
    return std::unique_ptr<T, Deleter<T>>(t, Deleter<T>(df));
}

int main(){
    int i = 42;

    {
        auto intHolder = CreateUniquePtr(i, [](int i){
            std::cout << i << std::endl;
        });
    }
}

C++0xすごい! Move Semantics最高!ということで個人的なまとめなど。

右辺値参照について詳しく知りたい場合は
http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx
とか
http://d.hatena.ne.jp/ntnek/20090210/p1
とかへどうぞ。

なお、以下のコードは私の理解している物なので間違っているかもです。
VC++2010 Expressで動作確認を行っています。
また、RVOとかNRVOとかは無視しているので最適化によっては別の動作をする事があります。


こんなクラスを作ってみます。

using std::string;

class C
{
public:
 explicit C(const string& s) : s(s){}

 //copy
 C(const C& c) : s(c.s){}

 //copy
 C& operator=(const C& c){
  //copy and swap
  C temp(c);

  std::swap(*this, temp);

  return *this;
 }

 //move
 C(C&& c) : s(std::move(c.s)){}

 //move
 C& operator=(C&& c){
  s = std::move(c.s);

  return *this;
 }

//今やthrowしないswapは必要ない

private:
 string s;
};

このクラスにたいして、こんな事が出来ちゃいます。

C create(const string& s){
 return C(s);
}

int main(){
 C c1 = create("c1");
}

今までなら例外安全性がどうとか言われる所ですが……
create関数内でCのコンストラクタが呼ばれて、作成されたオブジェクトを返します。
そのときにmoveコンストラクタが呼ばれ、c1が初期化されるので、create関数内でオブジェクトの作成が成功した場合、例外が発生することはありません。
(moveは例外を投げないようにしないといけないため)

また、関数内でインスタンスのコピーが必要な場合、以下のように書くと

void copy_use(C c){
 //処理
}

void use(const C& c){
 //処理
}

void out(C& c){
 //処理
}

int main(){
 C c1("c1");
 C& c1ref = c1;

 copy_use(c1);   //1
 copy_use(c1ref);  //2
 copy_use(create("c2")); //3

 use(c1);    //4
 use(c1ref);    //5
 use(create("c2"));  //6

 out(c1);    //7
 out(c1ref);    //8
 //compile error!
 //out(create("c2")); //9
}

1、2の場合はコピーコンストラクタが呼び出されてコピーが作成されますが、3の場合はmoveコンストラクタが呼ばれて余分なコピーは作成されません。
また、今まで通り引数をただ参照したいだけなら4、5、6が、引数の書き換えも行いたい場合は7、8が使用できます。
残念ながらVC++だと9が「非標準の拡張」としてコンパイルが通っちゃうのですけど。警告レベル4にしないと教えてくれないあたり意地悪です。

ちなみに、throwしないswapが必要ないのは、std::swapがmoveを使用してくれるので、そもそも例外がでない上に効率が良いからです。

moveコンストラクタとmove代入演算子は、なるべく上記に習って作成した方がいいです。
なぜなら、規格でこの二つの関数が自動生成されるみたいだからです。
http://d.hatena.ne.jp/faith_and_brave/20100331/1270020213
VC++2010ではまだ実装されていないようですが、将来的に自動生成された物に置き換えられるように……ということです。
後、生ポインタを持っていると、自動生成されるmoveコンストラクタとかmove代入演算子が危ないので、
メンバには生ポインタではなくstd::unique_ptrやstd::shared_ptrに入れた方が良さそうです。標準ですし。
※特にこのあたり、誤解がありそうです。ポインタを持っているクラスなんてたくさんありそうですし、互換性とか大丈夫なのでしょうか……と思うのですけど。まあ、そんなことを行っているプログラムがバグのないまともなプログラムとも思えませんが……

あと、やっぱりVC++2010にはありませんが、こんな物もあるみたいです。
http://d.hatena.ne.jp/faith_and_brave/20081204/1228382167
具体的にどういう事が出来て何がうれしいのかがよくわからないので、とりあえずメモ書きだけ。
 

添付ビヘイビア自体については他のサイトにお任せします。
MVVMパターンを使用したときに、イベントとVMのコマンドをBindingしたいときに使えるみたいです。

以下のコードはWPF Model-View-ViewModel Toolkitを使用しています。

まずはXAMLです。

<!--前略-->
<Grid>
 <ListView Name="listView" ItemsSource="{Binding Path=Table}">
  <ListView.View>
   <GridView>
    <GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Name" />
    <GridViewColumn DisplayMemberBinding="{Binding Path=Age}" Header="Age" />
   </GridView>
  </ListView.View>
 </ListView>
</Grid>
<!--後略-->

いろいろは省略しますが、このGridViewの選択行が変わったときにメッセージボックスを出してみようと思います。
ついでにNameの値を拾ってみたりも。

まずはコマンドを用意します。
VMに以下のコードを追加します。

//前略
private DelegateCommand<string> changeNameCommand;

public ICommand ChangeNameCommand
{
 get
 {
  if (changeNameCommand == null)
  {
   changeNameCommand = new DelegateCommand<string>(ChangeName);
  }
  return changeNameCommand;
 }
}

private void ChangeName(string s)
{
 MessageBox.Show(s);
}
//後略

普通にコマンドを作成します。

次に添付ビヘイビアを作成します。

//前略
namespace WpfModelViewTest.Behavior
{
 class ListViewBehavior
 {
  #region SelectionChangedCommand

  public static readonly DependencyProperty SelectionChangedCommandProperty =
   DependencyProperty.RegisterAttached(
    "SelectionChangedCommand",
    typeof(ICommand),
    typeof(ListViewBehavior),
    new UIPropertyMetadata(null, OnSelectionChangedCommandPropertyChanged));

  public static ICommand GetSelectionChangedCommand(DependencyObject obj)
  {
   return (ICommand)obj.GetValue(SelectionChangedCommandProperty);
  }

  public static void SetSelectionChangedCommand(DependencyObject obj, ICommand value)
  {
   obj.SetValue(SelectionChangedCommandProperty, value);
  }

ここまではほぼ定型文です。

  static void OnSelectionChangedCommandPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  {
   ListView listView = obj as ListView;
   if (listView == null)
   {
    return;
   }

   ICommand command = (ICommand)e.NewValue;
   if (command != null)
   {
    listView.SelectionChanged += new SelectionChangedEventHandler(OnSelectionChanged);
   }
   else
   {
    listView.SelectionChanged -= new SelectionChangedEventHandler(OnSelectionChanged);
   }
  }

ここで、SelectionChangedイベントが発生したときに関数を実行するようにします。
ListViewのイベントであれば、SelectionChanged以外も使用できます。

  static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  {
   ListView listView = sender as ListView;
   ICommand command = ListViewBehavior.GetSelectionChangedCommand(listView);
   if (command != null)
   {
    var rowView = listView.SelectedItem as DataRowView;
    var row = rowView.Row as NameAgeDataSet.DataTableRow;
    if (row == null)
    {
     command.Execute("拾えませんでした");
    }
    else
    {
     command.Execute(row.Name);
    }
   }
  }
  #endregion
 }
}

で、イベントが発生したときにコマンドとNameの値を取得して、コマンドを実行します。

最後に、XAMLを修正します。

<Window x:Class="WpfModelViewTest.Views.MainView"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:c="clr-namespace:WpfModelViewTest.Commands"
 xmlns:l="clr-namespace:WpfModelViewTest.Behavior"
 Title="Main Window" Height="400" Width="800">

重要なのはxmlns:lの行です。
添付ビヘイビアを作成したNamespaceを指定します。

<!--中略-->
<Grid>
 <ListView l:ListViewBehavior.SelectionChangedCommand="{Binding Path=ChangeNameCommand}" Name="listView" ItemsSource="{Binding Path=Table}">
  <ListView.View>
   <GridView>
    <GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Name" />
    <GridViewColumn DisplayMemberBinding="{Binding Path=Age}" Header="Age" />
   </GridView>
  </ListView.View>
 </ListView>
</Grid>
<!--後略-->

で、添付ビヘイビアにVMのコマンドをBindingします。

以上です。

あまり良くない方法だと思いますが、複数のイベントとコマンドをBindingしてみたサンプルをおいておきます。
http://blog.cnobi.jp/v1/blog/user/2eef8264e2b1da90a110941b349819ff/1274362947

Twitter
Twitter Update
ブログ内検索
カレンダー
05 2025/06 07
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
最新コメント
最新トラックバック
プロフィール
HN:
100poisha
性別:
非公開
アクセス解析
Copyright © 100poishaのブログ All Rights Reserved.
Designed by north sound
Powered by Ninja Blog

忍者ブログ [PR]