可変長引数を再度わたすには II – プログラミング – Home

可変長引数を再度わたすには II
 
通知
すべてクリア

[解決済] 可変長引数を再度わたすには II


bun
 bun
(@bun)
ゲスト
結合: 24年前
投稿: 761
Topic starter  

便乗質問になってしまうのですが、話が変わるので別スレッドを起こします。
可変長引数をとる関数内から、可変長引数を取る関数を直接呼ぶ方法は存在する
のでしょうか?
前々からの素朴な疑問ですので、ご存じの方がいらしたら教えていただけると幸
いです。
以下のようなものです。
void TEST1(LPCTSTR Fmt, ...)
{
TEST2(Fmt, ...);
}

void TEST2(LPCTSTR Fmt, ...)
{
}

このままでは、もちろん、
error C2059: 構文エラー : '...'
となります。

よろしくお願いいたします。


引用未解決
トピックタグ
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

C/C++ ソースレベルでの移植性を保ったまま、では存在しない。
自分でアセンブラ手書きするとか、そういうことをすれば可能だろうけど。

というわけで、可変個数引数関数を作る場合は FormatV なり vprintf なりの
va_list を受け取る関数を同時に用意するのが常套手段なわけだ。

っていうか例えば CString のソースを見るとわかるけど
void CString::Format(LPTCSTR format, ...) {
va_list arglist;
va_start(arglist, format);
FormatV(format, arglist);
va_end(arglist);
}
のように実装するのが再利用性を増すためにとられる手段なわけだ。
FormatV なり vprintf なりのほうが本体処理を担当。
Format や printf など ... を直接受け取る関数はただの Wrapper にする、
ってことだな。発想を逆にしておくといいよ。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

自分で可変長引数を扱おうと思ったら
tetrapodさんが書かれているva_listとva_start、va_endを使う事になります。
これについては上記のキーワードで調べて見てください。
使用例が書いてあるホームページもあると思いますから。

可変長引数の場合、可変長部分はデータ型を特定できないので
printf等のようにフォーマット文字列を使ってどういうデータ型のデータが
どういう順番でわたってくるのかと言うのを示してあげないと取り出せないです。
で、この部分を自分で実装するとなるとものすごくめんどくさい事になるので
通常は自分で組んだりはしないです。
tetrapodさんが書かれているようにvprintf等の関数を使って
実績のある実装を使う方法が一般的だと思います。

あえて、自分で実装してみようというのであれば、止めませんが、
かなりめんどくさいことは確かです。
一回自分でやってみるとなぜ、フォーマット文字列を間違えると
プログラムが落ちてしまうのか実感できるかも。


返信引用
bun
 bun
(@bun)
ゲスト
結合: 24年前
投稿: 761
Topic starter  

やはり、簡単な方法は無いのですね。

可変長引数の内部処理は、以前に少し追ったことがあります。
単純に複数の引数がメモリ上の連続領域として取られて(引数スタック?)、それを順に
解析していたような記憶があります。
だから、連続領域の先頭アドレスを扱うと何とかなるのかな?
と思ったのですが、甘かったようです。


返信引用
金魚ちゃん
 金魚ちゃん
(@金魚ちゃん)
ゲスト
結合: 16年前
投稿: 52
 

>だから、連続領域の先頭アドレスを扱うと何とかなるのかな?
>と思ったのですが、甘かったようです。
この気持ちは分かります。
でも処理系によって引数スタックの積み方が違うじゃんね。
左順からとか、右順から評価されたりするから。

C/C++の規格では既定されていないので
コンパイラを特定してインライン・アセンブラを使えば
出来そうですが可読性がない、保持性もない、移植性はまったくなくなるね。

僕も興味深くこの質問を見ていましたが
やっぱりスマートな方法はなさそうですね。
僕も納得。
(若い頃、vsprintf のミニ版を作ったが面倒でしたよ)


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

可変長引数の所って通常の関数で言う参照渡しではないから
だと思います。ここの説明はかなり面倒です。

可変長引数の所に関しては、引数として引き渡された値が
単純にスタック上に連続で詰まれますよね。
引き渡した値が何かのポインタだったとしてもそれに関しては
お構い無しです。要はアドレスを示す数値が値渡しで積まれます。
つまり、可変長引数の場合は引数で渡された内容が値渡しで
引き渡されるわけです。なので、可変長引数の内容が格納されている
変数のアドレスを引き渡されたとしても可変長引数が求める引き渡し方に
なっていない事になります。
可変長引数で渡された内容をそのまま呼び出される側に引き渡す必要が
ある為、上記のような特殊な渡し方になるわけです。


返信引用
tetrapod
 tetrapod
(@tetrapod)
ゲスト
結合: 21年前
投稿: 830
 

何が言いたいのかさっぱりわからん・・・
変数のアドレスを引き渡す可変個数引数関数などごく普通にある scanf とか

俺が言いたいのは
Format と FormatV のペア、printf と vprintf のペアのように
可変個数引数 ... を直接受け取る関数と、va_list を受け取る関数とを
ペアで作っておくべし(自分で作るなら)、ということだけ。

逆に、ペアになっていないようなライブラリがあったら俺はその作者の技量を疑う。
まともなライブラリなら ... と va_list の両者の関数が必ずペアで提供されている
はずなので、そもそも可変個数引数関数から可変個数引数を直接呼ぶ必然がない。
... な関数からは va_list な引数を呼べば事足りる、っつこと。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

>だから、連続領域の先頭アドレスを扱うと何とかなるのかな?
>と思ったのですが、甘かったようです。

に対する書込みです。

要は引き渡し方が特殊だからそういう引き渡し方では無理ですねと言う話。

tetrapodさんの書き込みは私も賛成でそういう風に作っておけば、
使い回しも聞くでしょと言う話ですよね。
あんまり自分でゴリゴリ作るところではない気はしますね。
可変長引数だと型チェックも出来ないし。
この手のが必要なのってデバッグ時のログ取り用の関数くらいしか
思いつかないしなぁ。


返信引用
PATIO
(@patio)
Famed Member
結合: 3年前
投稿: 2660
 

scanfの場合は引き渡された値をフォーマット文字列の内容に合わせて
どういう変数のポインタか判断して処理しているのではないかなぁ。
受け取る側がva_list内の値の解釈をして判断していると言う意味では
同じだと思うんですが。

実際にはあんまり重要ではない話かもしれないですねぇ。

tetrapodさんの言うように
「Format と FormatV のペア、printf と vprintf のペアのように
可変個数引数 ... を直接受け取る関数と、va_list を受け取る関数とを
ペアで作っておくべし(自分で作るなら)、ということだけ。

逆に、ペアになっていないようなライブラリがあったら俺はその作者の技量を疑う。
まともなライブラリなら ... と va_list の両者の関数が必ずペアで提供されている
はずなので、そもそも可変個数引数関数から可変個数引数を直接呼ぶ必然がない。
... な関数からは va_list な引数を呼べば事足りる、っつこと。」

で十分かも。


返信引用

返信する

投稿者名

投稿者メールアドレス

タイトル *

プレビュー 0リビジョン 保存しました
共有:
タイトルとURLをコピーしました