ラベル Programming の投稿を表示しています。 すべての投稿を表示
ラベル Programming の投稿を表示しています。 すべての投稿を表示

2013年12月14日土曜日

初めてテンプレートメタプログラミングに触れてみた

初めてテンプレートメタプログラミングというものに触れた。 正直今でも何が起こっているのかは理解できてない。 以下はテンプレートメタプログラミングで整数の整数乗を求めるプログラム。 尚、型は全てint。

#include <iostream>

// default
template< int base, int iexp, bool = iexp >= 0 >
struct Pow{
	// fallback for iexp < 0
	static const int value = 0;
};

// specialization for iexp >= 0
template< int base, int iexp >
struct Pow< base, iexp, true >{
	// base ^ iexp = base * base ^ ( iexp - 1 )
	static const int value = base * Pow< base, iexp - 1 >::value;
};

// specialization for iexp == 0
template< int base >
struct Pow< base, 0, true >{
	// base ^ 0 = 1
	static const int value = 1;
};

int main( void ){
	std::cout << "2 ^ -2 = " << Pow< 2, -2 >::value << std::endl;
	std::cout << "2 ^ -1 = " << Pow< 2, -1 >::value << std::endl;
	std::cout << "2 ^  0 = " << Pow< 2,  0 >::value << std::endl;
	std::cout << "2 ^  1 = " << Pow< 2,  1 >::value << std::endl;
	std::cout << "2 ^  2 = " << Pow< 2,  2 >::value << std::endl;

	return 0;
}
コンパイル時定数では浮動小数点実数は扱えない(constexpr等の例外を除く)らしい(嘘をついているかも)ので、 扱う変数valueの型はstatic const int型としている。

そのため、負数乗は全て0とした。 iexpが0未満の時は全てデフォルトの定義になる。

0乗は1、自然数乗は再帰を行うように定義している。

よく分かっていないのは bool = iexp >= 0 という式。 型だけ書いて変数名(識別子)を書かないのはテンプレートだから許されるのだろうか。 おそらく、関数のプロトタイプ宣言をする時に、形名だけを書くのと似たようなものだろうと思っている。 どちらも同じ「引数」だし。

あと、2つ目の定義と3つ目の定義のどちらを使用するのかをコンパイラがどうやって決めているのかもよく分かんない。 C++怖い。

あ、間違いあったら教えて下さい。

2013.12.16追記 ※constexprが使える環境ならconstexprを使うべきと思われる。
このコードはMSVC++2008で書いたのでconstexprは使えなかった。

2013年11月5日火曜日

C++の名前空間のネストと識別子の衝突と無名名前空間について

私は普段DXライブラリを使っているのだが、 先日、以下の様なコードを書いた所エラーが出た。 ちなみに簡略化された全く意味のないコートであることに留意されたし。

#include "lua.hpp"
#include "DxLib.h"

namespace Lua{
	int Func( lua_State* pLuaState ){
		return 0;
	}
}
namespace DxLib{
	namespace Lua{
	}
}
void Test( void ){
	lua_State* p = NULL;
	Lua::Func( p );
}

エラーの内容は以下のとおり。

error C2872: 'Lua' : あいまいなシンボルです。
'Lua' である可能性があります。
または 'DxLib::Lua'

DXライブラリは、 DxLib.h 内で、名前空間 DxLib を using 宣言している。 このため、名前空間 DxLib::Lua と 名前空間 Lua をコンパイラが区別する事ができず、 コンパイルエラーとなっている。

C++において、普通に宣言した識別子は、無名名前空間(グローバル名前空間とも呼ばれる)に所属している。 無名名前空間に属している事を明示するためには、識別子をスコープ演算子から書き始める。 この記法を用いて修正したコードは以下のとおり。(関数 Test のみ)

void Test( void ){
	lua_State* p = NULL;
	::Lua::Func( p );
}

本当なら、関数 Func は名前空間 Lua にのみ存在するので、参照自体は出来ると思うのだが・・・ もしかしたらVC++ 2010でコンパイル出来ないだけで、他のコンパイラだと通るかもしれない。C++怖い。

ちなみに、以下の様な、識別子を使わない名前空間も無名名前空間と呼ぶ。

namespace{
	...
}

この名前空間内に記述した識別子は、同じソースコード内でしか参照できなくなる。 C++では、 static 修飾子よりも無名名前空間を使用することが推奨されている[要出典]

2013年11月4日月曜日

Luaを使ってクラスっぽい物を生成してCから使う方法 修正版

2013.11.04 追記

以前の投稿が激しく誤った情報を載せていたので修正。

Luaでクラス風のオブジェクトのメンバ関数を呼ぶ際は、 lua_pushvalue 関数を用いて、オブジェクトそのものを引数としてスタックにプッシュしてやる必要がある。

今回はメンバ関数内でオブジェクトのメンバを書き換えていないため、これでも正しく動作するが、 もしメンバ関数内でメンバを書き換える場合は、オブジェクト自身を return し、 lua_replace 関数を用いて返り値を元のオブジェクトに上書きしてやる必要がある。詳細は別途記事を書く予定。
2013.11.04 昨日はそうしないとうまく動かなかった筈ですが今日やったら動きました。 lua_pushvalue でコピーしたテーブルの内容を書き換えた場合って、参照元も書き換わるのかな… よく分からんのでもし分かる人がいらっしゃったらコメントとかで教えて下さい。

main.cpp

#include <lua.hpp>
#include <stdio.h>

int main( void ) {
	lua_State* L = luaL_newstate();	// Luaステートを生成
	luaL_openlibs( L );				// 標準ライブラリを読み込み

	// スクリプトの読み込み
	if( luaL_dofile(L, "script.lua") ){
		// エラー処理
		printf( "%s\n", lua_tostring( L, lua_gettop(L) ) );
		lua_close( L );
		return 0;
	}

	// 犬を生成
	lua_getglobal( L, "Dog" );					// グローバルなテーブル"Dog"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Neko" );				// 名前はNeko。犬なのにNeko。
	lua_pcall( L, 1, 1, 0 );					// 実行。関数と引数はスタックから除去され、返り値が積まれる
	lua_remove( L, -2 );						// 返り値の下にあるDogを除去
	int idx_dog = lua_gettop( L );				// Nekoのスタック上の位置を取得

	// 猫を生成
	lua_getglobal( L, "Cat" );					// グローバルなテーブル"Cat"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Inu" );					// 名前はInu。猫なのにInu。
	lua_pcall( L, 1, 1, 0 );					// 実行。関数と引数はスタックから除去され、返り値が積まれる
	lua_remove( L, -2 );						// 返り値の下にあるCatを除去
	int idx_cat = lua_gettop( L );				// Inuのスタック上の位置を取得
	
	printf( "%d object\n", lua_gettop( L ) );	// スタックのサイズが正しい事を確認

	// メンバ関数showを呼び出し
	lua_getfield( L, idx_dog, "show" );	// 関数をスタック
	printf( "%d object and function\n", lua_gettop( L ) );
	lua_pushvalue( L, idx_dog );		// 引数selfをスタック
	printf( "%d object, function, and argument\n", lua_gettop( L ) );

	// 実行
	if( lua_pcall( L, 1, 0, 0 ) ){
		// エラー出力
		printf( "%s\n", lua_tostring( L, lua_gettop( L ) ) );
	}
	
	printf( "%d objects\n", lua_gettop( L ) );
	
	// メンバ関数showを呼び出し
	lua_getfield( L, idx_cat, "show" );	// 関数をスタック
	printf( "%d object and function\n", lua_gettop( L ) );
	lua_pushvalue( L, idx_cat );		// 引数selfをスタック
	printf( "%d object, function, and argument\n", lua_gettop( L ) );

	// 実行
	if( lua_pcall( L, 1, 0, 0 ) ){
		// エラー出力
		printf( "%s\n", lua_tostring( L, lua_gettop( L ) ) );
	}
	
	printf( "%d object\n", lua_gettop( L ) );

    lua_close( L );	// 終了
    return 0;
}

script.lua

-- Dog
Dog = {}
Dog.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the DOG.", self.name )
		print( s )
	end
	return obj
end

-- Cat
Cat = {}
Cat.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the CAT.", self.name )
		print( s )
	end
	return obj
end

2013年11月1日金曜日

BloggerでSyntaxHighlighterを利用する際のTIPS

前の記事で、SyntaxHighlighterを導入してソースコードをBlogger上で表示した。 Luaの表示には公式ウェブサイトからリンクされているサイトの追加ブラシのLua1を利用した。 SyntaxHighlighterを使う際に以下のような問題に遭遇したため、ここにその対処法を示す。
  • Bloggerでは任意のファイルをアップロード出来ない
  • 記事上で、<等の特殊文字がうまく表示できない
  • タブが半角スペースに置換され、インデントがずれる
  • 全ての行の背景が同じ色で表示されるため、少し見辛い
  • shThemeXxx.css と shCoreXxx.css の違いが分からない

Bloggerでは任意のファイルをアップロード出来ない

Bloggerでは、任意のファイルをアップロードする事はできない。よって、JavaScriptやCSSをBloggerのドメインでホスティングする事は出来ず、Alex氏がホスティングしているファイルを参照する事になる。
別にそれはそれで構わないのだが、出来れば自分でホスティングしたい。それに、Syntax Highlighterがサポートしていない形式のコードを扱う時は困る(これも、その作者がホスティングしてくれているファイルを参照すれば問題ないが、精神的によろしくない)。
Google サイトを使った方法や、Google ドライブを利用する方法等があるようだが、いずれも欠点を抱えているようなので、私のオススメはDropboxを利用することである。
ここで注意したいのが、DropboxでのファイルのURLを生成する方法は二種類あることだ。
Publicフォルダに置いたファイルは「公開リンクをコピー」することで、そのURLを取得出来る。この方法で取得するURLには一意なユーザIDと、Publicフォルダ内におけるディレクトリ情報が含まれる。複数のハンドルネームを使っている場合等、「このファイルとあのファイルが置かれているアカウントは同じアカウントだ」とバレたくなければ使わないほうがいいだろう。ただし、内部的に相対パスを利用するファイルではこちらの方法が必須となる。
尚、生成されるURLは https://dl.dropboxusercontent.com/u/userID/filename だ。
Publicフォルダ以外に置いたファイルは「Dropbox リンクを共有」の機能を使用することで、URLを生成することができる。こちらの方法で生成したURLは、どのアカウントのファイルなのかを含め、隠蔽される。相対パスは利用できないが、よりセキュアであると言える。
生成されるURLは https://www.dropbox.com/s/fileID/filename だ。
参考:

記事上で、<等の特殊文字がうまく表示できない

HTMLでは一部の特殊文字は文法上の記法に利用されるため、エスケープシーケンスが必要となる。 置換にはこのサイトを利用した。
HTML特殊文字変換|コードをホームページに載せる時に便利 | すぐに使える便利なWEBツール | Tech-Unlimited 他にもあるかも知れないし、ローカルで出来るようなプログラムがあった方が良いかもしれない。

タブが半角スペースに置換され、インデントがずれる

Bloggerでは、タブが半角スペースに勝手に置換されるため、ソースコードのインデントがずれる。 プレビューではちゃんと表示されるのに、保存、公開した途端に置換されるのが曲者だ。 投稿時の編集のモードをHTMLにし、タブを &#9; に置換して記述することで、この現象を避けることが出来る。 ただし、一度編集のモードを作成にすると、せっかく置き換えたタブが半角スペースに戻ってしまうため注意。 公開後、きちんと投稿後の記事を確認した方が良いだろう。

全ての行の背景が同じ色で表示されるため、少し見辛い

これは、SyntaxHighlighterのCSSを編集することで改善できる。 .syntaxhighlighter .line.alt1 と .syntaxhighlighter .line.alt2 のそれぞれに背景色を設定してやればよい。 詳細は Syntax Highlighterの背景色と行間を変更する | ジャイアントモリンキーのjavaテック を参照。

shThemeXxx.css と shCoreXxx.css の違いが分からない

shCoreXxx.css は shCore.css と shThemeXxx.css を合成したものである。 参考: そらとぶくじら。 SyntaxHighlighter

以上。

2013年10月22日火曜日

Luaを使ってクラスっぽい物を生成してCから使う方法

2013.11.04 修正版記事を作りました。
2013.11.04 大きな間違いに気がついたので後日書き直します。本記事は大嘘です。
2013.11.01 誤って記事を消してしまったので書き直し。

本記事は間違いだらけです。修正版はこちら。


以下のようなコードによって、Luaで簡単なクラス風のテーブルを作成できる。

クラス風のテーブルを作成する方法はWEB上に多数のサンプルが見受けられたが、 Cとの連携を付記したコードは見当たらなかったので備忘録として作成。

継承にはもう少し工夫が必要だが、複雑なテーブルを作成するならC++で普通にクラスを書いたほうが良い気がする。主観だが。

Luaでグローバル領域に宣言されたテーブルのフィールドを利用する際は、一度テーブルをスタックに積んでからフィールドを取得する点に注意。

main.cpp

#include <lua.hpp>
#include <stdio.h>

int main( void ) {
	lua_State *L = luaL_newstate();	// Luaステートを生成
	luaL_openlibs( L );				// 標準ライブラリを読み込み
	
	// スクリプトの読み込み
	if (luaL_dofile(L, "script.lua") ) {
		// エラー処理
		printf("%s\n", lua_tostring(L, lua_gettop(L) ) );
		lua_close(L);
		return 0;
	}

	// まず犬を生成
	lua_getglobal( L, "Dog" );					// グローバルなテーブル"Dog"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Neko" );				// 名前はNeko。犬なのにNeko。
	lua_pcall( L, 1, 1, 0 );					// 実行!
	int idx_dog = lua_gettop( L );				// Nekoのスタック上の位置を取得

	// 次に猫を生成
	lua_getglobal( L, "Cat" );					// グローバルなテーブル"Cat"をスタックに積む
	lua_getfield( L, lua_gettop( L ), "new" );	// トップに積まれたテーブルのメンバ"new"をスタックに積む
	lua_pushstring( L, "Inu" );					// 名前はInu。猫なのにInu。
	lua_pcall( L, 1, 1, 0 );					// 実行
	int idx_cat = lua_gettop( L );				// Inuのスタック上の位置を取得

	lua_getfield( L, idx_dog, "show" );	// 関数をスタック
	lua_pcall( L, 0, 0, 0 );			// 呼び出し
	
	lua_getfield( L, idx_cat, "show" );	// 関数をスタック
	lua_pcall( L, 0, 0, 0 );			// 呼び出し

    lua_close( L );	// 終了
    return 0;
}

script.lua

-- Dog
Dog = {}
Dog.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the DOG.", name )
		print( s )
	end
	obj:show()
	return obj
end

-- Cat
Cat = {}
Cat.new = function( name )
	local obj = {}
	obj.name = name
	obj.show = function( self )
		s = string.format( "%s the CAT.", name )
		print( s )
	end
	obj:show()
	return obj
end

2013年8月23日金曜日

glBeginとglMaterialfvの順番とGLSLの関係

OpenGLで描画を行う際、
glBeginよりも後にglMaterialfvを呼び出すと、
シェーダーを使用した際に色が設定されない。
シェーダーを使用しなかった場合は正しく描画されてしまうのが曲者である。

// NG
glUseProgram( shaderProgram );
glBegin( GL_POLYGON );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red );
glVertex3f( 0.0, 0.0, 0.0 );
glVertex3f( 1.0, 0.0, 0.0 );
glVertex3f( 1.0, 1.0, 0.0 );
glVertex3f( 0.0, 1.0, 0.0 );
glEnd();

// OK
glUseProgram( shaderProgram );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, red );
glBegin( GL_POLYGON );
glVertex3f( 0.0, 0.0, 0.0 );
glVertex3f( 1.0, 0.0, 0.0 );
glVertex3f( 1.0, 1.0, 0.0 );
glVertex3f( 0.0, 1.0, 0.0 );
glEnd();

shaderProgramとredは書いていないが適切な変数だと思って頂きたい。

恐らく、glBeginの時点でGPUにカラー情報が渡され、後から変更できない為に、
このような挙動になっているのではないかと。
シェーダーを使用しない場合何故通ってしまうのかは不明。

描画するオブジェクトの種類によっては、
glBeginとglEndの間で普通にglMaterialfvを呼び出すオブジェクトもあるが、
そういったオブジェクトでglBeginとglEndの間でglMaterialfvを間で呼び出すと、
シェーダーを使用する際どうなるかは調べていないので不明。
もしかしたら環境によって挙動が異なる可能性すらある。

2013年7月11日木曜日

Luaの標準ライブラリのロード方法

今まで一部で躓いていたCからのLuaステートへの標準ライブラリのロードがうまくいったのでアウトプット。

以下はうまくいかないコード。
#include

int main( void ) {
    lua_State *L = luaL_newstate();
    luaopen_package( L );
    lua_close( L );
    return 0;
}

出力:
PANIC: unprotected error in call to Lua API (no calling environment)

以下が正しいコード。
#include

int main( void ) {
    lua_State *L = luaL_newstate();
    lua_pushcfunction(L, luaopen_package);
    lua_pushstring(L, LUA_LOADLIBNAME);
    lua_call(L, 1, 0);
    lua_close( L );
    return 0;
}

リファレンスを見る限りは、全てのライブラリのロードで下の方法を用いるべきと思われる。上の方法でうまく動くライブラリとそうでないライブラリが存在する。
luaL_openlibs( L ); // OK
luaopen_base( L ); // OK
luaopen_math( L ); // OK
luaopen_string( L ); // OK
luaopen_table( L ); // OK
luaopen_debug( L ); // OK
luaopen_package( L ); // NG
luaopen_io( L ); // NG
環境によるかもしれないので必ず上を使いましょう。

2013年7月10日水曜日

luaopen_mathでスタックに謎のLUA_TTABLEが積まれる件について

以下のコードは動作はするが、予期せぬ結果となる。

script.lua

function func(num)
    return num;
end

main.cpp

#include "lua.hpp"
int main( void ) {
    lua_State* p = luaL_newstate();
    luaopen_math( p );
    luaL_dofile( p, "script.lua" );
    int num = 100;
    lua_getglobal( p, "func" );
    lua_pushnumber( p, num );
    lua_pcall( p, 1, 1, 0 );
    num = lua_tonumber( p, 1 );
    lua_close( p );

    return 0;
}

最終的にnumに入っている値は100であると予想されるが、
実際には0が入っている。
luaopen_mathを呼び出した際に、謎の LUA_TTABLE がプッシュされるため、
スタックの一番下には func の返り値ではなく、
テーブルが入っており、その上に返り値が積まれている。

NG: num = lua_tonumber( p, 1 );
OK: num = lua_tonumber( p, -1 );

何が積まれているのかは調べたけど分からなかった。
誰か、Luaに詳しい方で分かる方いらっしゃったら教えて下さい。

2013年2月9日土曜日

DXライブラリとLuaを使ってスクリプト側で画像をロード

内容はタイトルの通り。
ソースコードは こちら 。
Lua向けに専用のLoadGraph関数を用意してやり、それをlua_Stateに渡し、
lua_State側からその関数を呼び出して画像をロードするプログラム。

コードの説明はプログラム内のコメントと、コメントに書いてあるリンク先の参考文献を参照のこと。

記事内では、プロジェクトの設定について説明する。
まず、 DXライブラリ置き場 使い方説明 に従って、DXライブラリが動くプロジェクトを作成する。
DXライブラリがうまく動いたら、 その1 Luaのインストール に従って、Luaのセットアップをする。
リンク先では、コンソールアプリケーション向けの設定についての記述なので、Win32アプリケーションで行う設定を以下に列挙する。

  1. 追加のインクルードディレクトリにluaフォルダ内のincludeフォルダへのパスを追加
  2. 追加のライブラリディレクトリにluaフォルダへのパスを追加
  3. リンカ->入力->追加の依存ファイルにlua5.1.libを追加
  4. lua5.1.dllをプロジェクトフォルダの直下にコピー
以上である。

主要な参考文献