c++で弾幕風における”yield”の実現

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Sigma
記事: 20
登録日時: 8年前

c++で弾幕風における”yield”の実現

#1

投稿記事 by Sigma » 8年前

タイトル通り弾幕風のようなyieldで処理を止めて別の処理に移り、
次のループで自分に処理順がきたときに処理の続きをするといったタスクシステムを作りたいのですが、
組み方の検討がつかずに困っています。

一応listでタスクを格納するコンテナを作って処理させようとしています。
タスクを作る、全タスクの更新と描画、いらないタスクの消去の仕組みはできるのですが、
途中で別タスクの処理に移る処理でつまずいてます。

自分的にはカウンタとswitchをつかうような記述はしたくないです。

コード:

void task::update()
{
    switch(mCount)
    {
    case 0:
        /*1ループ目の処理*/
        break;
    case 1:
        /*2ループ目の処理*/
        break;
    //以下略
    }
    mCount++;
}
↑こういう感じではなく弾幕風のような

コード:

void task::update()
{
    /*1ループ目の処理*/
    yield;  //ここで別タスクに移動して次のループまで処理しない
    /*2ループ目の処理*/
    wait(10);  //10フレーム待機してはじめに戻る
}
void wait(int w)
{
    for(int i=0; i<w; i++) yield;
}
みたいな処理をしたいです。

頻繁に申し訳ないですがよろしくお願いいたします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#2

投稿記事 by softya(ソフト屋) » 8年前

コルーチンみたいな処理ですね。
boostでこんなのならあるみたいです(使った事無いです)。
「[C++]Boost.Coroutine」
http://blogs.wankuma.com/melt/archive/2 ... 30217.aspx
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

めるぽん

Re: c++で弾幕風における”yield”の実現

#3

投稿記事 by めるぽん » 8年前

Boost.Coroutine は正式に Boost の仲間入りしたわけではないです。どちらかというと、今は Boost.Fiber の方が注目されていますね。
更に、コルーチンの要となるコンテキストスイッチの部分だけまずは Boost 入りさせようという動きになっていて、今後どうなるかは分からないです。
まあ正式に入っていないというだけで、十分実用にはなるので、使ってもいいとは思います。

あとは Boost.Asio の作者が作った、マクロによるコルーチンの実装を使うという方法もあります。
http://d.hatena.ne.jp/faith_and_brave/2 ... 1286432629

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#4

投稿記事 by Sigma » 8年前

分かりやすそうなのでめるぽんさんの案をやってみようと思いますが、
この方法って

コード:

class task : coroutine
{
    task():
    i(0)
    {}
    void update()
    {
        reenter(this) {
            i++;
            yield return;
            i *= i;
            yield return;
        }
    }
private:
    int i;
}
コレでメンバ関数のupdateを実行するようにしてもちゃんと中途開始ができますか?
あと下まで行ったときに次にupdateを実行するときにはじめに戻りますでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#5

投稿記事 by softya(ソフト屋) » 8年前

試したほうが速いと思いますが、試せない環境なのでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
a5ua
記事: 199
登録日時: 9年前

Re: c++で弾幕風における”yield”の実現

#6

投稿記事 by a5ua » 8年前

std::function(もしくは、boost::function)を使って、1フレームの処理をvectorとかに入れておいて、
updateで順番に呼び出せば、それっぽいことはできそうな気がする。(以下に示すコードは、sequential_coroutineがそれに相当する)

さらに、指定回数の繰り返しとか、無限に繰り返すとかも作ってみたので、参考までにどうぞ
► スポイラーを表示

アバター
御津凪
管理人
記事: 200
登録日時: 9年前
住所: 道内
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#7

投稿記事 by 御津凪 » 8年前

弾幕風の "yield" に当たるコルーチン(マイクロスレッドとも呼ばれる)の処理は Win32API にファイバーと呼ばれる機構で、ある程度同じように実装可能ですよ。

※以下、ファイバー機構を使う際の説明です
► スポイラーを表示
他の方法としては、自分でコルーチン機能をもつスクリプトを構築するとかがありますね。
オフトピック
ちなみに、私の開発中ライブラリに、C++ネイティブで"yield"に相当する機能を提供するコルーチンライブラリ"Coro"を同梱してたりしてます。
(開発版として公開している中に同梱してますが、お勧めはしません)
This article was written by "Mitsunagi".

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#8

投稿記事 by Sigma » 8年前

今boostを導入していますが、
何故か正しくパスも通したはずなのに"yield.hpp"がないと言われてしまってます…

ファイバーについて調べたらこっちも弾幕風のyieldと近い感覚でできそうですが、
クラスに対応できるのかという心配があります。

めるぽん

Re: c++で弾幕風における”yield”の実現

#9

投稿記事 by めるぽん » 8年前

Sigma さんが書きました:今boostを導入していますが、
何故か正しくパスも通したはずなのに"yield.hpp"がないと言われてしまってます…
URL の内容をちゃんと読んでください。yield.hpp は、Boost.Asio の example として存在しているだけです。
Boost の include へパスを通しても無意味なので、example からファイルをローカルにコピーするなりして使ってください。
Sigma さんが書きました:ファイバーについて調べたらこっちも弾幕風のyieldと近い感覚でできそうですが、
クラスに対応できるのかという心配があります。
Boost.Coroutine, Boost.Fiber は(Windows 上では)Win32API のファイバー関数を利用しているので、それを参考にすればいいと思います。
ただ Boost.Coroutine, Boost.Fiber を使った方が断然楽だとは思いますが。

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#10

投稿記事 by Sigma » 8年前

長らく放置してしまって申し訳ありません。
yieldの実装について試行錯誤をしてましたが、擬似コルーチンの手法を見つけ今実装中です。
http://d.hatena.ne.jp/izmktr/20120220/1329745486#c

しかし試しては見たものの、「error C2051: case 式は、整数型定数でなければなりません。」と言われてしまいました。
試しにswitch文のcaseに__LINE__を入れただけのものを実行してみると同じエラーが吐かれました。
もしかして環境によってcaseに__LINE__が使えないのでしょうか…

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#11

投稿記事 by softya(ソフト屋) » 8年前

少なくともリンク先のコードはVC++2008ではエラーにはなりません。
開発環境とコードをを明示してもらった方が良いかもしれません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#12

投稿記事 by Sigma » 8年前

環境はVC++2010Expressです。
DXLibを参照させてあります。

コード:

//先ほどのリンク先にあるコルーチンクラスを継承させたクラスの1関数
void EffNowLoading::UpdateTask()
{
	CoroutineBegin();
	for(int i=0; i<64; i++){
		if(mMoveB>544)mMoveB -= 64/15;
		yield;	//この行で先ほどのエラーが起きてしまう
	}
	for(int i=0; i<64; i++){
		mBrinkA += 360/45;
		mBrinkB += 360/60;
		yield;
	}
	CoroutineEnd();
	return;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#13

投稿記事 by softya(ソフト屋) » 8年前

とりあえずコンパイルが通るものを書いて見ましたが、forのiの値が保証されないのでどんな動作をするかは保証出来ないと思います。
元のサンプルのクラスのメンバ変数にiがあるのは意味がある事なのでよく考えてみて下さい。

コード:

#include <stdio.h>

#ifndef CoroutineHeader
#define CoroutineHeader

class Coroutine
{
protected:
	int state;
public:
	Coroutine(): state( 0 ) {}
	virtual ~Coroutine() {}
};

#define CoroutineBegin() switch(state){case 0:
#define CoroutineEnd()   default: break;}
#define yield {state = __LINE__; return false; case __LINE__:;}

#endif


class Test: public Coroutine
{
	int i;

public:
	bool Foo() {
		CoroutineBegin();
		for ( i = 1; i <= 20; i++ ) {
			printf( "%d ", i );
			yield;
			if ( ( i % 3 ) == 0 ) {
				printf( "fizz " );
				yield;
			}
			if ( ( i % 5 ) == 0 ) {
				printf( "buzz " );
				yield;
			}

		}
		CoroutineEnd();
		return true;

	}

	int mMoveB;
	int mBrinkA;
	int mBrinkB;
	bool UpdateTask()
	{
		int i;
		CoroutineBegin();
		    for(i=0; i<64; i++){
		        if(mMoveB>544)mMoveB -= 64/15;
				yield;
		    }
		    for(i=0; i<64; i++){
		        mBrinkA += 360/45;
		        mBrinkB += 360/60;
				yield;
		    }
		CoroutineEnd();
	    return true;
	}
};

int main( void )
{
	Test test;
	for ( ;; ) {
		bool finish = test.Foo();
		if ( finish ) break;
		printf( "---\n" );

	}

	return 0;
}

by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#14

投稿記事 by Sigma » 8年前

もしかしてと思い、コルーチンの定義・コルーチンを使用する関数を同じファイルにした所通りました。
これってファイル分割できないのですね・・・
となると割と不便。。。

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#15

投稿記事 by Sigma » 8年前

一応通ったコードを載せます。

コード:

#ifndef _EFFNOWLOADING_H_
#define _EFFNOWLOADING_H_

#include "DxLib.h"
#include "../Effect/FallParticle.h"
#include "../Utility/Coroutine.h"
#include "../Loader/LoadData.h"
#include <list>
using namespace std;

#define CoroutineBegin switch(state){case 0:
#define CoroutineEnd   default: break;}
#define yield {state = __LINE__; return; case __LINE__:;}

class EffNowLoading : public Coroutine
{
public:

//--------------------------------------------------------------------------------------------------------
//            省略
//--------------------------------------------------------------------------------------------------------

	//=======================================================================//
	/**
 	 * 更新タスク
 	 */
	//=======================================================================//
	void UpdateTask(){
		CoroutineBegin;
		loop(15){
			if(mMoveB>544)mMoveB -= 64/15;
			yield;
		}
		loop(300){
			mBrinkA += 360/45;
			mBrinkB += 360/60;
			yield;
		}
		CoroutineEnd;
		return;
	}

private:
	//文字画像描画
	Primitive2D mPrimA;
	Primitive2D mPrimB;
	//モーション制御用
	float	mMoveB;
	float	mBrinkA;
	float	mBrinkB;
	//文字画像格納用
	int	mImgHnd;

	//ブロックコンテナ
	list< FallParticle* > mPtcContainer;
	//ブロックイテレーター
	list< FallParticle* >::iterator mItr;
};

#endif
どうやら

コード:

#define CoroutineBegin switch(state){case 0:
#define CoroutineEnd   default: break;}
#define yield {state = __LINE__; return; case __LINE__:;}

コード:

	void UpdateTask(){
		CoroutineBegin;
		loop(15){
			if(mMoveB>544)mMoveB -= 64/15;
			yield;
		}
		loop(300){
			mBrinkA += 360/45;
			mBrinkB += 360/60;
			yield;
		}
		CoroutineEnd;
		return;
	}
この2つが同じファイルに居ないとエラーを起こすみたいです。
さらにファイル分割でcppに記述しても上記2つが同じファイルにあってもエラーを吐きます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#16

投稿記事 by softya(ソフト屋) » 8年前

ファイルを分離してみましたがコンパイル通りますよ。

CoroutineHeader.h

コード:

#ifndef CoroutineHeader
#define CoroutineHeader
 
class Coroutine
{
protected:
    int state;
public:
    Coroutine(): state( 0 ) {}
    virtual ~Coroutine() {}
};
 
#define CoroutineBegin() switch(state){case 0:
#define CoroutineEnd()   default: break;}
#define yield {state = __LINE__; return false; case __LINE__:;}
 
#endif
main.cpp

コード:

#include <stdio.h>

#include "CoroutineHeader.h"
 
class Test: public Coroutine
{
    int i;
 
public:
    bool Foo() {
        CoroutineBegin();
        for ( i = 1; i <= 20; i++ ) {
            printf( "%d ", i );
            yield;
            if ( ( i % 3 ) == 0 ) {
                printf( "fizz " );
                yield;
            }
            if ( ( i % 5 ) == 0 ) {
                printf( "buzz " );
                yield;
            }
 
        }
        CoroutineEnd();
        return true;
 
    }
 
    int mMoveB;
    int mBrinkA;
    int mBrinkB;
    bool UpdateTask()
    {
        int i;
        CoroutineBegin();
            for(i=0; i<64; i++){
                if(mMoveB>544)mMoveB -= 64/15;
                yield;
            }
            for(i=0; i<64; i++){
                mBrinkA += 360/45;
                mBrinkB += 360/60;
                yield;
            }
        CoroutineEnd();
        return true;
    }
};
 
int main( void )
{
    Test test;
    for ( ;; ) {
        bool finish = test.Foo();
        if ( finish ) break;
        printf( "---\n" );
 
    }
 
    return 0;
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#17

投稿記事 by ISLe » 8年前

余計なことかもしれませんけど…。

Coroutineクラスを継承したクラスでメンバ関数ひとつしかコルーチンを実装できないのでは。
Fooメンバ関数とUpdateTaskメンバ関数を交互に呼ぶと誤動作しますよね。
交互に呼ばないなら良いですけど。

Sigma
記事: 20
登録日時: 8年前

Re: c++で弾幕風における”yield”の実現

#18

投稿記事 by Sigma » 8年前

やってみましたが確かに通りました。
どうやらyieldを使う関数の宣言と定義を別ファイルにするとエラーをはかれるようです。

複数起動できないという点は今気づきました…
先ほどの方法を改良すればいいらしいですが、
私的にはコルーチンを通すための値を作りたくなくて、
あくまで(例えば)taskと関数に宣言するくらいで出来るようにしたいです。
今まで調べてたものもそれが一番のネックで実装を躊躇してましたが、これしか無いのであれば諦めは付くのですが…

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#19

投稿記事 by softya(ソフト屋) » 8年前

コルーチン・マクロ見る限り継承で作らずにコルーチン・マクロを改造してコルーチンのstateを管理するクラスのインスタンスを指定できるように作れば良いように感じます。
コルーチンが必要な数だけstateを管理するクラスのインスタンスを作ってはダメなのでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2648
登録日時: 9年前
連絡を取る:

Re: c++で弾幕風における”yield”の実現

#20

投稿記事 by ISLe » 8年前

実装したいコルーチンの数だけCoroutineを継承した関数オブジェクトを作るという方法もありますね。
メンバ関数っぽく呼び出せますし。

閉鎖

“C言語何でも質問掲示板” へ戻る