ページ 1 / 1
関数オブジェクトを配列に入れる
Posted: 2008年12月28日(日) 20:35
by kaiten
お久しぶりです。
現在vista+VC++2008EEでプログラミングしているのですが不明な点があったので質問させていただきます。
関数オブジェクトについてなのですが、int型で作った関数オブジェクトを関数ポインタのようにint型の配列に代入して使用することはできないのでしょうか?
試しにやってみたのですが期待どおりの動作をしてくれませんでした。
試したときのソース載せておきます。
//--bullet.h--
#pragma once
#include "header.h"
#include "class.h"
class AroundShot
{
public:
int operator()(int);
};
class Bullet : public Object
{
private:
static const int shot_num=1;
int player;
int power;
int type;
int img;
public:
AroundShot AS;
int Shot_Set[shot_num];
Bullet(){}
Bullet(float x, float y, int lifemax, float angle, int r, float speed, int power, int player);
Bullet(const Bullet ©, const Object &O_copy);
~Bullet();
virtual void Move();
};
extern std::vector<Bullet> bullet;
//--Bulletのコンストラクタ--
Bullet::Bullet(float x, float y, int lifemax, float angle, int r, float speed, int power, int player) :
Object(x, y, lifemax, angle, r, 0), power(power), player(player), type(::player[playe[/url].multi), img(Bullet_Img[playe[/url][::player[playe[/url].multi])
{
/*
this->power=power;
this->player=player;
this->type=::player[playe[/url].multi;
this->img=Player_Img[playe[/url][::player[playe[/url].multi];
*/
Shot_Set[0]=AS(0);
GetGraphSize(img, &w, &h);
this->w/=2;
this->h/=2;
}
//AroundShot
int AroundShot::operator()(int)
{
printfDx("ASop was used");
return(0);
}
while(ProcessMessage() == 0&&GetHitKeyStateAll(key) == 0){
ClsDrawScreen(); //裏画面のデータを全て削除
fps.FPS_Wait();//フレームの待機を計算
//実際の処理を入れる(初期化などを除く
switch(state)
{
case TITLE:
state=SELECT;
break;
case SELECT:
player[0]=Player(0, 0, 100, border_x/2, 400, 100, 0, 3, 5, 3, 0, 0);
player[1]=Player(1, 0, 100, border_x/2, 80, 100, 180, 3, 5, 3, 0, 0);
bullet[0].Move();
bullet[0].Shot_Set[0];
//printfDx("%d",bullet[0].AS(1));
ScreenFlip();
state=MAIN;
break;
}
fps.Draw_FPS();
ScreenFlip(); //裏画面データを表画面へ反映
}
#題名忘れてました。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月28日(日) 21:31
by Justy
>int型で作った関数オブジェクトを関数ポインタのようにint型の配列に代入して使用することはできないのでしょうか
あんまり意味がよくわからないのですが、要するに
「int型を戻り値とする関数オブジェクトを実行して、その戻り値を int型の配列に代入できるか」
ということですか?
そうであるならできます。
>試しにやってみたのですが期待どおりの動作をしてくれませんでした
該当する行は
[color=#d0d0ff" face="monospace]Shot_Set[0]=AS(0);[/color]
でしょうか?
特に問題なく実行できそうな気がしますが、何を期待していたのに
どうなってしまったのでしょうか?
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月28日(日) 22:14
by kaiten
いえ、そうではないんです。
いくつかの関数オブジェクトをまとめて一つの配列のようにしたいんです。配列から関数オブジェクトを呼べるように。
たとえば
Shot_Set[0]=AS(0);
としたら、
bullet[0].Shot_Set[0]とbullet[0].AS(0)が同じ意味になるようにしたいんです。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 01:04
by Justy
>いくつかの関数オブジェクトをまとめて一つの配列のようにしたいんです。配列から関数オブジェクトを呼べるように
なんとなくやりたいことはわかりました。
>bullet[0].Shot_Set[0]とbullet[0].AS(0)が同じ意味になるようにしたいんです
つまり、関数オブジェクトは AroundShotだけに限らす他のものであるかもしれない、と。
で、int型の引数で int型の戻り値を持つ関数オブジェクトを配列として扱いたい、ということですね?
であれば、boostの
http://www.boost.org/doc/libs/1_37_0/do ... ction.htmlが使えるのではないでしょうか。
ただ、前者のようにX番目の配列としてアクセスしただけで関数オブジェクトが実行される
ということになりますと単純な配列ではできませんが、配列 operatorを使えば
なんとか近いものが実装できそうです。
(代入は bindに頼っているのでちょっとアレですが)
[color=#e0f0ff" size="-1" face="monospace]
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <vector>
#include <functional>
class AroundShot1 : public std::unary_function<int, int>
{
int m;
public:
AroundShot1(): m(0) {}
int operator()(int v) { return v * 100 + m++; }
};
class AroundShot2 : public std::unary_function<int, int>
{
public:
int operator()(int v) { return v * 200; }
};
class Hoge
{
typedef std::vector< boost::function<int ()> > func_list_type;
func_list_type funclist;
public:
Hoge(std::size_t arraySize): funclist(arraySize) {}
template <typename Func>
void Set(std::size_t pos, Func f) { funclist.at(pos) = f; }
int operator[/url](std::size_t pos)
{
func_list_type::value_type &func = funclist.at(pos);
return func? func(): 0;
}
};
class Object { };
class Bullet : public Object
{
static const std::size_t shot_num = 3;
public:
AroundShot1 AS;
Hoge Shot_Set;
public:
Bullet()
: Shot_Set(shot_num)
{
AS(0); // AroundShot1の関数オブジェクトを呼び出す (メンバ mが 1になる)
Shot_Set.Set(0, boost::bind(boost::ref(AS), 1)); // メンバ変数 ASを使う
Shot_Set.Set(1, boost::bind(AroundShot2(), 2)); // 一時変数からコピーして使う
printf("ASop was used 0, %d\n", Shot_Set[0]); // 結果は 101(メンバ ASの mも変化する)
printf("ASop was used 1, %d\n", Shot_Set[1]);
}
};[/color]
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 01:05
by SCI
関数ポインタ風ということなので、C言語風に書くと
int (*farray[10])(int);
farray[0] = &func;
farray[0](0); /* func(0) と同値 */
というようなことでしょうか?
vector<AroundShot>にpush_backではダメですか?
> bullet[0].Shot_Set
[0]とbullet[0].AS
(0)が同じ意味
operator[/url]を使って引数を指定したいということならオーバーロードですが・・・
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 01:08
by SCI
&funcじゃなくてfuncだった。
しかも投稿被った・・・
boostは使ったことないですが、オススメです(何
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 01:56
by Justy
>vector<AroundShot>にpush_backではダメですか?
関数オブジェクトが AroundShotだけならそれもいいですね。
あ、そうだ。
それみて思いつきましたが、オーソドックスに AroundShotなどは operator()を仮想関数にした
インターフェースクラスから継承して作り、インターフェースへのポインタと引数をセットにして
vectorに入れればAroundShot以外であってもいけそうです。
あれ? 関数オブジェクトで有る必要が無くなってしまった……。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 02:22
by Justy
ちょっと追加。
Hogeのメンバ関数に
[color=#d0d0ff" face="monospace]
template <typename Func>
void Set(std::size_t pos, const Func &f, int arg)
{
funclist.at(pos) = boost::bind(f, arg);
}
[/color]
というのがを加えておくと、設定するときに
[color=#d0d0ff" face="monospace]
Shot_Set.Set(0, boost::ref(AS), 1);
Shot_Set.Set(1, AroundShot2(), 2);
[/color]
のようになりますので使うときに bindが消えるので楽になります。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 02:26
by SCI
はい、私も書き込んだ後に思いました。
なので、私も(引数セットではないですが)インターフェイスへのポインタをvectorに入れて・・・は考えました。
確かに、これだと関数ポインタと変わらないですね・・・
一説によると、最適化でインラインになるから関数呼び出しオーバーヘッドよりちょっと速くなるという噂は聞いたことがありますが(笑)
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 08:42
by kaiten
寝てる間にこんなにレスが……本当にありがとうございます。
口下手ですみません。でも何とか通じたようでよかったです。
STLのアルゴリズムで関数オブジェクトが必要なものがあるから、といった理由で使おうとしていたのですが、よく考えると関係ないですね。単に関数オブジェクトを挟めばいいだけ……
簡単なことだろうと思ってたので自分の技量を書いてなかったんですが予想よりはるかに高い山のようで……
しかし、Boostですか。まだSTLもロクに使えないんで敬遠してたのでちょっとビビってますw
今の自分に出来そうなのはインターフェースクラスから作ることでしょうかねぇ……試してみます。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 14:36
by Justy
SCIさん
>一説によると、最適化でインラインになるから関数呼び出しオーバーヘッドよりちょっと速くなる
関数オブジェクトをを使った方が、ということですか?
間接呼び出しで無くなる分はそうですね。
kaitenさん
>今の自分に出来そうなのはインターフェースクラスから作ることでしょうかねぇ
関数ポインタは、戻り値と引数が一致すれば別の関数でも型は同じになりますが、
関数オブジェクトは呼び出し方は同じでも別々になるので単純な配列化は難しいです。
実際 AroundShotの役割が判っていないので、それがベストなのか判りませんが
インターフェースを使う方法が一番楽かもしれません。
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 14:39
by Justy
(あんまり検証していないませんが)boost無しで書くとこんな感じになります。
まだちょっと敷居が高いかもしれませんが、雰囲気だけでも参考にということで。
[color=#d0d0ff" face="monospace]
class FuncObject
{
struct BaseHolder
{
virtual ~BaseHolder() {}
virtual BaseHolder * Clone() const = 0;
virtual int Execute() = 0;
};
template<typename Func>
class Holder : public BaseHolder
{
public:
Holder(const Func & func_, int arg_) : func(func_), arg(arg_) {}
virtual BaseHolder * Clone() const { return new Holder(func, arg); }
virtual int Execute() { return func(arg); }
private:
Func func;
int arg;
Holder & operator=(const Holder &);
Holder(const Holder& other);
};
template<class T> class RefWrapper
{
T* p;
public:
explicit RefWrapper(T& t): p(&t) {}
int operator()(int v) { return (*p)(v); }
};
BaseHolder * func_holder;
public:
FuncObject() : func_holder() {}
FuncObject(const FuncObject& other)
: func_holder(other.func_holder ? other.func_holder->Clone() : 0) {}
~FuncObject() { delete func_holder; }
template<typename ValueType>
FuncObject & operator=(const FuncObject & other)
{
delete func_holder;
func_holder = 0;
func_holder = other.func_holder->Clone();
return *this;
}
template<typename Func>
void Set(const Func &f, int arg)
{
func_holder = new Holder<Func>(f, arg);
}
template<typename Func>
void SetRef(Func &f, int arg)
{
func_holder = new Holder< RefWrapper<Func> >(RefWrapper<Func>(f), arg);
}
int Execute()
{
if(func_holder) return func_holder->Execute();
else return 0;
}
};
class Hoge2
{
typedef std::vector< FuncObject > func_list_type;
func_list_type funclist;
public:
Hoge2(std::size_t arraySize): funclist(arraySize, FuncObject()) {}
template <typename Func>
void Set(std::size_t pos, const Func &f, int arg) { funclist.at(pos).Set(f, arg); }
template <typename Func>
void SetRef(std::size_t pos, Func &f, int arg) { funclist.at(pos).SetRef(f, arg); }
int operator[/url](std::size_t pos)
{
func_list_type::value_type &func = funclist.at(pos);
return func.Execute();
}
};
[/color]
で、使うときはこうなります。
[color=#d0d0ff" face="monospace]
Shot_Set.SetRef(0, AS, 1);
Shot_Set.Set(1, AroundShot2(), 2);
printf("ASop was used 0, %d\n", Shot_Set[0]);
printf("ASop was used 1, %d\n", Shot_Set[1]);
[/color]
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月29日(月) 16:15
by たかぎ
Boost C++ Librariesを使ってよいのであれば、次のようにすればもっと簡単になるかもしれません。
#include <iostream>
#include <boost/any.hpp>
class function
{
boost::any f_;
int (*call_)(const boost::any&, int);
template <class F>
static int call(const boost::any& f, int arg)
{
return boost::any_cast<F>(f)(arg);
}
public:
function() : call_(0) {}
template <class F>
function(F f) : f_(f), call_(&call<F>) {}
int operator()(int arg) const { return (*call_)(f_, arg); }
};
int foo(int arg)
{
return arg + 1;
}
int main()
{
function a[10];
a[0] = &foo;
for (int i = 0; i < 3; i++)
std::cout << a[0](i) << std::endl;
}
Boost.Functionより理解しやすいかと思いますが、いかがでしょうか?
Re:関数オブジェクトを配列に入れる
Posted: 2008年12月30日(火) 10:20
by kaiten
関数オブジェクトの形は諦めてインターフェースクラスを使う方法で期待道理の動作になったのでとりあえず解決とさせていただきます。
教えていただいたBoostなどの方法はもっとC++やSTLを理解してから再度チャレンジしてみようと思います。
皆さまのおかげで解決できました。ありがとうございます。