動的仮引数?について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
bass_clef_

動的仮引数?について

#1

投稿記事 by bass_clef_ » 4年前

件名に記述しといてなんなのですけど、現在しようとしていることが動的仮引数なのかどうかがいまいちわからないので間違ったところがあればご指摘いただければと思います。

で、今しようとしているものについてですけど、
LoadLibrary, GetProcAddress で取得したポインタに対して 呼び出しをしたいのですけど、その時に引数を動的に変えたいのです
変えるものというのは 引数の数,引数の型,関数名 という品目で本来ユーザーでなくソースにて記述するべき項目です。

具体的な例をあげると

コード:

typedef void* (__stdcall *DLLFUNC)(...);
HMODULE hModule = LoadLibrary("user32.dll");
DLLFUNC func = (DLLFUNC)GetProcAddress(hModule, "MessageBoxA");
こういう初期化があったとして、関数ポインタ func に対して前述の項目を動的に変えたいと思うのです。
できれば配列の中身を勝手に引数にしてくれると嬉しいのですけど、、、

ひとつ方法を見つけた?のですけど、引数の数が固定されるのだけは動的に変えられないようなので質問をしました。
どうぞよろしくお願いします。

一応私のc++歴? は wikiの規格を読むくらいで、最近ようやっとc++11で遊び始めました。

コード:

// 上記funcに対してこれで一応型と関数名までは動的に変えられる?,数も定義すれば何個でもいける?(無確認)
	std::array<long long, 20> values = { 0, (long long)"text", (long long)"title", MB_OK };
	func(
		values[0],
		values[1],
		values[2],
		values[3],
		values[4],
		values[5],
		values[6],
		values[7],
		values[8],
		values[9],
		values[10],
		values[11],
		values[12],
		values[13],
		values[14],
		values[15],
		values[16],
		values[17],
		values[18],
		values[19]
	);

djann
記事: 27
登録日時: 6年前

Re: 動的仮引数?について

#2

投稿記事 by djann » 4年前

C++11を学び始めているということなので、そちらの機能を利用しつつ回答します。

例えどんなに動的にしたいと思っていても、自身でコードに落とした以上それはコンパイル時に確定出来る情報となりますので、故にコード中で一見動的な引数を与えるファンクションコールは可能と言えば可能です。
例えば以下のコードのような手法となります。お試しください。

コード:

#include <Windows.h>


class library_caller {
	HMODULE m_module;	// モジュールの保持.
public:
	// コンストラクタにてモジュールを読み込む.
	library_caller( char const *module_name )
		: m_module( LoadLibraryA( module_name ) )
	{
	}
	~library_caller(){
		FreeLibrary( m_module );
	}

	template <class RetType, class ...Args>
	RetType invoke( char const *proc_name, Args ...args ){
		// dllの関数を、invoke()に渡された型ど同期する関数ポインタ型と認識しキャストする.
		// ちなみに__stdcall以外の呼び出し規約だとこのコードでは破綻するので注意が必要.
		typedef RetType(__stdcall *call_type)(Args...);
		call_type module = reinterpret_cast<call_type>(GetProcAddress( m_module, proc_name ));

		return module( args... );
	}
};

// 使用例.
int main()
{
	// user32.dllの呼び出し.
	library_caller caller("user32.dll");

	caller.invoke<int>("MessageBoxA", nullptr, "Text", "Caption", MB_OK );
}
こちらの例では、第一引数に呼び出す関数の名前を与え、その後その関数に渡す引数を与えて実行するという形にしてあります。なお戻り値の型は呼び出しの際にテンプレート引数で与える形にしています。
void*を経由すれば、関数の名前を与える部分とその実際の呼び出し部分を分けることも可能となりますが、ただでさえ誤認が起こりやすいのにそれをさらに増長することになりかねないので、この例では一緒くたにしてあります。

なおこちらのコードは可変長引数テンプレートを用いている関係上、Visual Studioであれば2015以降でないとコンパイル出来ませんのでご了承ください。
※Visual Studio 2015とClang 3.7にて動作確認済み。

もしC++03以前の環境で同様の挙動を行わせたいというのであれば、invoke()メンバをオーバーロードし可変長引数テンプレートの挙動をエミュレートするとよいでしょう(丁度Visual Studio 2013以前のstd::tupleやstd::functionのような形)。

bass_clef_

Re: 動的仮引数?について

#3

投稿記事 by bass_clef_ » 4年前

>jdannさん
ご回答いただいてありがとうございます。

やはりコンパイルする言語なので動的に引数の型や数を変更するには字句解析とかするしかないのでしょうか……

djann
記事: 27
登録日時: 6年前

Re: 動的仮引数?について

#4

投稿記事 by djann » 4年前

bass_clef_ さんが書きました:>jdannさん
ご回答いただいてありがとうございます。
惜しい、djannです!
bass_clef_ さんが書きました:やはりコンパイルする言語なので動的に引数の型や数を変更するには字句解析とかするしかないのでしょうか……
前述のサンプルではやりたい事が足りなかったということでしょうか?C++上で統一的に関数呼び出しを行う(個別にキャストする必要なく)のが想定仕様かと勘違いしていました。

どのような事が行いたいのか、今一度教えていただけませんか?行いたい「使い方」をソースコードの形で出し、想定される動作等を補足するような形であればより伝わりやすいかもしれません。


※なお、既存のAPIに追加の引数を渡して呼び出したいといった事を行いたいのだとすると、API自体はその追加の引数を解釈する機構を持たないので無意味となりますし、先ほどのサンプルでも引数を追加して呼び出す事は可能ですが、Win32であればstdcall規約の為戻って来た際にコンテキストが破壊される事になります。

bass_clef_

Re: 動的仮引数?について

#5

投稿記事 by bass_clef_ » 4年前

ああー、本当に申し訳ないですdjannさん _(_ _)_

うまく伝えられないのですけど、関数の呼び出しに 関数ポインタ,引数の数,引数 のような形で呼び出せれば結果的にいいなと思ってるのです。
HSPという言語に callfunc という関数があるのですけど、これをC++でもできないかなと思った次第です、、

戻り値 = callfunc( 引数が格納されている配列, 関数アドレス, 引数の数 )

仮に callfunc が使えるとしてC++風に書いてみると

コード:

	auto hModule = LoadLibrary("user32.dll");
	auto funcAdd = GetProcAddress(hModule, "MessageBoxA");

	char *text = "text", *caption = "caption";
	long long ary[4] = { 0, (long long)text, (long long)caption, MB_OK };
	int paramCount = 4;

	// これで funcAdd (MessageBoxA) が呼べる
	long long resultValue = callfunc(ary, funcAdd, paramCount);

	FreeLibrary(hModule);
こんな感じになっております

ここではソースで変数などを静的に記述してますが
もちろん モジュール名、関数名、引数の数,型,内容 などすべて実行時に動的にユーザーによって指定できます(C++ でいえば std::cin,scanf とかで)

今回、HSPからC++へ自作の常駐ソフトを移植していた最中ですけど、ここで躓いてしまった次第です。
HSPは変数の定義とか,型とかが弱い言語ですけど OpenHSP といって内部は C言語で完結しているようなので
なんとか代価策が見つかればと思い、ここに質問しました、、

djann
記事: 27
登録日時: 6年前

Re: 動的仮引数?について

#6

投稿記事 by djann » 4年前

bass_clef_ さんが書きました:ここではソースで変数などを静的に記述してますが
もちろん モジュール名、関数名、引数の数,型,内容 などすべて実行時に動的にユーザーによって指定できます(C++ でいえば std::cin,scanf とかで)

とあるということは、他の情報源からモジュール名、関数名、引数の型リスト、そのパラメータ等をユーザから指定させるということですねかね?例えば文字列等で入力してもらったり、スクリプトの実行環境だったりして。

アバター
びす
記事: 31
登録日時: 8年前

Re: 動的仮引数?について

#7

投稿記事 by びす » 4年前

32bitでVC++ならこんな感じでできそうだけど
実用できる代物なのかはわからないです、いかにもクラッシュしそう(^^;

コード:

long callfunc(long* arg, void* func, int len){
	long result, tmp;
	// 引数
	for (int i = 0; i < len; i++) {
		tmp = arg[len-1-i];
		__asm push tmp
	}
	// 実行
	__asm call func
	// 戻り値
	__asm mov result, eax
	return result;
}
11/8 2:25編集 引数の順序が逆になってましたw

bass_clef_

Re: 動的仮引数?について

#8

投稿記事 by bass_clef_ » 4年前

>びすさん

あー、やはりアセンブラを書くことになりますか、、、

実行環境が64bitで作成していたので、アセンブラはなんか、書き方が違うんですよね、、、
やはり、レジスタを直接いじって実行するしかないんでしょうか

bass_clef_

Re: 動的仮引数?について

#9

投稿記事 by bass_clef_ » 4年前

>djannさん
そういうことになりそうです、

djann
記事: 27
登録日時: 6年前

Re: 動的仮引数?について

#10

投稿記事 by djann » 4年前

64bitでの作成だと、アセンブリ言語を用いるとしても手間がかかりそうですね。
基本的に引数はレジスタ渡しで、浮動小数点数と整数(ポインタ)は別のレジスタに設定する必要がありますし(いや、条件式一つ書くだけか・・・?)

ツール自体がそういう文字列から関数を呼び出すというものなら仕方ないのですが、そうでなければcallfuncと同じ形に無理やりするより正常なC++へ書き換えた方が手間は少なそうです。
オフトピック
一応C++のみでMessageBoxAの呼び出しが確認出来るコードは書けます。
自作ライブラリを使用しているのでこちらに貼っても意味がないと思うので、そのライブラリの一部も含め以下に置いてます。
http://djann.web.fc2.com/files/call_64.zip

bass_clef_

Re: 動的仮引数?について

#11

投稿記事 by bass_clef_ » 4年前

わかりました。
C++だけでは面倒そうなので、別の方法を取ろうと思います。
djannさん びすさん、ありがとうございました。

閉鎖

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