C言語は関数の多値返却を標準機能としてサポートできるか
C言語における多値返却は,
- 構造体を返す
- 関数の引数に参照を渡してそこに値を入れる
などが考えられるけど,
int, double f(void) { return 1, 0.1; } int x, double y = f();
みたいなコードの書き方をC言語標準としてサポートできるかという議論を Twitter で行ったのでそのまとめ.
のような問題が挙った.
結論
のように < > で囲めば構文解釈は容易らしいが,C++でテンプレート構文とコンフリクトしないかな? - オーバーロードの解決は可能らしい,計算は増えるが実用上は問題ないだろう.
- 引数に多値返却関数が使われた場合の挙動は適切に決める.とりあえずエラーでもいいのでは.
- 機械語レベルでは構造体の返却と同じ扱いで実装可能
- C言語標準機能として適切かはかなり疑問
- UNIX の記述言語であることを考慮すると,コンパイル時に返り値の数が決定されている必要がある(動的にメモリ確保を行わない)
- 標準じゃなくて良いなら Cyclone の Polymorphic Functions を使おう.
- C++だったら pair とか tuple を使おう.
Twitter のログ
xharaken | 昔から思ってることだが,C言語で一番気に入らない点は,関数の返り値が1個しか返せないこと.一般論として,「関数」とは複数の入力を受け付けて複数の出力を返すものなので,1個しか返り値を返せないのはあまりに不便. | link |
xharaken | このせいで,言語仕様に振り回されてコーディングが無駄に汚くなっているように感じることがよくある. | link |
yuyarin | C 言語が関数の返り値を1つまたは無し(void)しかサポートしない合理的な理由は何だろう. | link |
koizuka | @yuyarin いわゆるALGOL系の流れは大体「戻り値一つの関数」と「戻り値なしの手続き」だったんでは | link |
kodam | 再帰ができないから? RT @yuyarin : C 言語が関数の返り値を1つまたは無し(void)しかサポートしない合理的な理由は何だろう. | link |
xharaken | @yuyarin わかんない.現状のC言語でも大きい構造体をそのまま返り値にできるわけなので,サイズ的な制限が問題になるわけじゃないと思うんだけど. | link |
yuyarin | ALGOL 系という歴史的事情があったとしても,拡張は考えられていてもおかしくないと思う.オーバーロードで何か問題が起きるのかなぁ. | link |
yuyarin | できないかなぁ?できない例を考えてみます. RT @kodam : 再帰ができないから? RT @yuyarin : C 言語が関数の返り値を1つまたは無し(void)しかサポートしない合理的な理由は何だろう. | link |
GreenKei | @yuyarin 確かに気になりますね。ひとつの関数にはひとつの役割しか持たせるべきでないとかそんな感じでしょうか? | link |
kodam | @yuyarin 複数あるというイメージがもてないけど、2つ以上あったら、指数関数的に計算量が増えそう | link |
yuyarin | 入出力の記述方法の問題だと思います. RT @GreenKei : @yuyarin 確かに気になりますね。ひとつの関数にはひとつの役割しか持たせるべきでないとかそんな感じでしょうか? | link |
yuyarin | 確かにオーバーロードの解決に費やす計算は増えると思いますが...実用上はそれほどクリティカルではない気がします. RT @kodam : @yuyarin 複数あるというイメージがもてないけど、2つ以上あったら、指数関数的に計算量が増えそう | link |
kodam | @yuyarin なるほど。逆に複数返り値を持つ関数を扱える言語ってあるんですか? | link |
yuyarin | perl, python, ruby はできたと思います.スクリプト言語ですが. RT @kodam : @yuyarin なるほど。逆に複数返り値を持つ関数を扱える言語ってあるんですか? | link |
yuyarin | 複数返り値の実体は perl, ruby は配列,python はタプルだったっけ. | link |
mutaguchi | PowerShellも配列かなぁ 結局ArrayもTupleもオブジェクトじゃないかという気がしたり RT @yuyarin : 複数返り値の実体は perl, ruby は配列,python はタプルだったっけ. | link |
yuyarin | エラーコードを取得するのに引数の最後に boost::system::error_code & ec とか埋め込むのは気持ち悪いですよね. | link |
yuyarin | 呼び出し側で x, y = func(); みたいな記述ができないといけないな. | link |
yuyarin | 同じような議論がどこかで絶対行われているので探したいなぁ. | link |
flyingwktk | matlabとかだと帰り値の数は自由なんだけどねぇ。 RT: @yuyarin : 呼び出し側で x, y = func(); みたいな記述ができないといけないな. | link |
yuyarin | Q「複数の値を返すにはどうしたらいいですか?」A「構造体を返すとか引数で参照渡しとかをしてください」みたいなのばっかりひっかかるから難しい. | link |
hideaki_t | @yuyarin 多値が返せると便利な場面って多いですよね。名前つけるの面倒くさいので | link |
wraith13 | boost::tuple まわりで近いことできなかったっけ? RT @yuyarin : 呼び出し側で x, y = func(); みたいな記述ができないといけないな. [電波注意] | link |
wraith13 | @yuyarin だ、そうです。 RT @cpp_akira : . @wraith13 tie [電波注意] | link |
yuyarin | tie(x, y, z) = make_tuple(1,0.1, '1'); みたいに書けますね. RT @wraith13 : boost::tuple まわりで近いことできなかったっけ? RT @yuyarin : 呼び出し側で x, y = func(); みたいな記述 | link |
hrjn | funcA(funvcB(args))みたいのが出来ないからじゃない?引数のサポートの仕方も変えたりと意外と難しい要素がたくさんありそう。 RT @yuyarin : C 言語が関数の返り値を1つまたは無し(void)しかサポートしない合理的な理由は何だろう. | link |
yuyarin | どちらかというと funcA(1, funcB(), '1'); で funcB が複数返り値みたいな場合が問題になりそう. RT @hrjn : funcA(funvcB(args))みたいのが出来ないからじゃない?引数のサポートの仕方も変えたりと意外と難しい要素がたくさ | link |
hrjn | .@yuyarin そだね。rubyだと配列を引数に空気を読んで展開してくれたりして、結構想定しない動作する事ある。 | link |
yuyarin | はい,その通りですが,構造体を定義すること無く多値を返す記述方法を言語標準でサポートできないか考えています. RT @fujisho : @yuyarin 構造体を返せるので多値を返せると言ってもいいのでは > C言語 | link |
yuyarin | 結論: boost::tuple 使おうぜ | link |
hrjn | .@yuyarin 凄く冷静に考えれば、マシンコード的に二つの別々の場所のレジスタをさすのは難しいんじゃないかという事に気づいた。もしくは、あまり効率がよく無さそう。 | link |
yuyarin | 空気を読む必要があったり曖昧だったりした場合は全部コンパイルエラーで弾いてしまえばいい気がするけど,それによって不便になる場合を考えないとなぁ. RT @hrjn : .@yuyarin そだね。rubyだと配列を引数に空気を読んで展開してくれたりして、結構想定しない動作す | link |
yuyarin | 「さす」の主語がわからないのでなんとも言えませんが,レジスタ返しもスタック返しもできると思います. RT @hrjn : .@yuyarin 凄く冷静に考えれば、マシンコード的に二つの別々の場所のレジスタをさすのは難しいんじゃないかという事に気づいた。もしくは、あまり効率が | link |
hrjn | .@yuyarin もしくは、結局マシンコードレベルだと、構造体みたいなものを定義して値を返す構造になるだけなので、プログラミング言語作る様な低レイヤーのスーパーハカーはそれで何のメリットがあるのかわからないから実装しない。とかありそう。 | link |
yuyarin | マシンコードレベルなのに構造体みたいなものを定義して,というのはちょっとわかりません... RT @hrjn : .@yuyarin もしくは、結局マシンコードレベルだと、構造体みたいなものを定義して値を返す構造になるだけなので、プログラミング言語作る様な低レイヤーのスーパ | link |
yuyarin | そういえば double 型のマシンコードでの扱い方を知らない...勉強してくる. | link |
hrjn | .@yuyarin コンパイラが何をどうしてるのかよく知らないけど、単純に32bitにおさまる返り値を返すか、構造体なり配列なりtreeなりの先頭のポインタを返す方がシンプルで効率的だと思わない?ということ。 | link |
hrjn | .@yuyarin 例えば2つの返り値を返そうとすると、少なくとも2つの値を格納しているメモリか値へのポインタを持っている配列(連続した領域)を返すか、2つのメモリか値へのそれぞれのポインタを持っている領域へのポインタ(構造体)が必要なのでということ。日本語がなんかおかしい。 | link |
yuyarin | うーん...その考え方が必要になる箇所がよくわかりません... RT @hrjn : .@yuyarin 例えば2つの返り値を返そうとすると、少なくとも2つの値を格納しているメモリか値へのポインタを持っている配列(連続した領域)を返すか、2つのメモリか値へのそれぞれのポイン | link |
hrjn | .@yuyarin どうやって値返却させるかにもよるとは思うけど。少なくとも今言った方法は確実にあたい返却できるけど、素人考えなので効率的じゃないかもしれないし。どういうふうに関数の値返却ってするの? | link |
yuyarin | ほとんどの呼び出し規約では eax レジスタに入れておくだけです.構造体で返す場合は中身がどこかにコピーされてアドレスが eax で返ってきます. RT @hrjn : .@yuyarin どうやって値返却させるかにもよるとは思うけど。少なくとも今言った方法は確実にあたい返 | link |
yuyarin | edx とか ecx で値を返す場合の不都合ってあるのだろうか. | link |
yuyarin | 基本は構造体と同じようにスタックに積んで返せばいいと思うけど,__fastcall みたくレジスタで返してもいいよね,不都合が無ければ. | link |
hrjn | .@yuyarin うーん。eaxとかはよく知らないけど、別にレジスタである事には変わりないしな・・・・単純にそのeaxレジスタから変数が確保した領域にコピーをする時とかにちょっと面倒な気がしたんだけど。なんかそうでもない様な気もしてきた。 | link |
hrjn | これ以上はレジスタと関数と変数関連の値の移動の仕方とか、返り値が入っている場所をさすポインタをどう持ってるのかとか真面目に調べないとわからなそう。 | link |
hrjn | ただ返り値が一つである事で、効率化させる事は出来ると思うんだよね。例えば返り値用のスタックをあらかじめある程度とっておくとかは明らかに返り値が一つの方が効率的に実装できる。返り値がn個になると、別途に返り値の数を管理する構造が必要だったり結局構造体返すのと同じにする等しな | link |
yuyarin | 構造体を返す場合でもその処理は変わらないですよね. RT @hrjn : .@yuyarin うーん。eaxとかはよく知らないけど、別にレジスタである事には変わりないしな・・・・単純にそのeaxレジスタから変数が確保した領域にコピーをする時とかにちょっと面倒な気がしたんだけ | link |
yuyarin | コンパイル時に比較的複雑な処理になるため非効率と言うことは出来るけど,実行時に性能が構造体の返り値以上に落ちることは無いと思う. RT @hrjn : ただ返り値が一つである事で、効率化させる事は出来ると思うんだよね。例えば返り値用のスタックをあらかじめある程度とっておくと | link |
hrjn | .@yuyarin えぇっと、やっと通じた。TL的には2回目の同じ発言だけど、結局多値返却したところで、マシンコードになおる頃には同じになっている気がするし、プログラムの書き方によってはコンパイラがうまく最適化できなかったりして、あまりメリットがないんじゃないのと言いたかった。 | link |
hrjn | .@yuyarin 結局その「コンパイル時に比較的複雑な処理になる」という部分で、ちゃんと何とかなるのかなというところの疑問が言いたかった。俺のボキャぶらりの貧弱さが半端ない。 | link |
hrjn | @shinji_kono 素人質問でで申し訳無いのですが、そういうのってどこで規定されてるんですか?? | link |
shinji_kono | @hrjn 返り値の問題は、複数の値をどこに書けば良いかだけ。入力の場合は細かく規定されている。出力も細かく規定すれば良いだけ。一つにする利点はなに一つない。 | link |
hrjn | 結局こういうことっぽいのかな? RT @shinji_kono : @hrjn 返り値の問題は、複数の値をどこに書けば良いかだけ。入力の場合は細かく規定されている。出力も細かく規定すれば良いだけ。一つにする利点はなに一つない。 | link |
hrjn | というよりも、そもそもコンパイラの問題なので「比較的複雑な処理になる」という部分が肝心なんだと俺は思っていたという話。 | link |
yuyarin | 元は構造体をわざわざ定義せずに多値を返したいということなので,マシンコードもその最適化も構造体と同じで良いのです. RT @hrjn : .@yuyarin えぇっと、やっと通じた。TL的には2回目の同じ発言だけど、結局多値返却したところで、マシンコードになおる頃には同じに | link |
yuyarin | @hrjn やっと話が通じましたねw | link |
yuyarin | 僕の最初のポストはまさにこの疑問なのですよ. RT @hrjn : .@yuyarin 結局その「コンパイル時に比較的複雑な処理になる」という部分で、ちゃんと何とかなるのかなというところの疑問が言いたかった。俺のボキャぶらりの貧弱さが半端ない。 | link |
Cryolite | これ,発端は @yuyarin さんか. http://twitter.com/yuyarin/... | link |
yuyarin | . @Cryolite さらにその発端はこちらです http://bit.ly/Twl3Z | link |
yuyarin | 結局コンパイラとか言語処理がわかってないので結論はでないか...でもなんとかなりそうなきもする. | link |
pi8027 | @yuyarin ドラゴンブックとか読みましょう。 | link |
koizuka | @yuyarin C言語に関しては「高級アセンブラ」として、最低限の組み合わせで何でもできる、という目的には、さくっと「コンパイラが書けて」、ちゃんとうごく、というのは重要だったんじゃないかなー | link |
koizuka | @yuyarin あと「カーネルなどのシステムプログラミング」目的には、動的メモリ確保関数は使えないので、言語ではなくライブラリ扱い。従って動的メモリ管理が必要な要素は言語には組み入れない、というのもありそう | link |
yuyarin | なるほど,そういう点では標準として採用するのはハードルが高そうですね. RT @koizuka : @yuyarin C言語に関しては「高級アセンブラ」として、最低限の組み合わせで何でもできる、という目的には、さくっと「コンパイラが書けて」、ちゃんとうごく、というのは重要だ | link |
yuyarin | 多値返却を実装するなら,僕の考えでは構造体と同じ扱い(スタック返し)をすればいいので,動的メモリ確保(malloc等のことですか?)は必要ないと思います. RT @koizuka : @yuyarin あと「カーネルなどのシステムプログラミング」目的には、動的メモリ確保関数 | link |
koizuka | @showyou うん、だからそのmallocを排除する意味で言ってたんだけど、「言語」じゃ範囲が広すぎたな。「構文」と「ライブラリ」を合わせて「言語」というなら、「構文」の構成要件にしない、という感じ | link |
koizuka | @yuyarin 多値返却、といったときに、「動的に個数が決定される任意個の返却」を想定してしまっていたが、単純にコンパイル時決定なら問題はないか。 | link |
koizuka | @yuyarin あとは「式」のデザインの問題になるか。決め事だから趣味の問題もありそうだよなあ。「実用言語」として作ってたようだし | link |
koizuka | @yuyarin 「実用言語」ってまた妙な言葉を作ってしまった。つまりUNIXを動かすために作った言語、ということで、それさえ達成できればよかったんだろうし | link |
yuyarin | あ,はい.とりあえずはコンパイル時に返す値の個数が決定できるものを想定しています. RT @koizuka : @yuyarin 多値返却、といったときに、「動的に個数が決定される任意個の返却」を想定してしまっていたが、単純にコンパイル時決定なら問題はないか。 | link |
yuyarin | PHP の preg_match みたいな動的に返り値(出力)の個数が決まる関数は難しいなぁ. | link |
koizuka | C言語に多値返却が「当初無い」理由と、「今もなお無い」理由はまた違うか。"void関数"なんか後から付いたんだから、多値を入れる余地もまたあったはずだな | link |
koizuka | 多値の扱いは「構造体で実現」とみなしたのかな。 | link |
koizuka | 構造体の実体コピーなんて「一見して重そう」だからC言語書きにはキモイよねw | link |
koizuka | 実際にはコンパイラが細工して隠し引数ポインタ経由したりしてコピーを減らしいても、それが予想しにくいしな | link |
koizuka | C++の話じゃなくてCの話ね。 | link |
yuyarin | RT @ranha : 標準のCを変えたいって言うんじゃなければ勝手に変えれば良いだろうし、勝手に変わってるのがさいくろんでそ http://bit.ly/g6dfT | link |
koizuka | @t_yano inline関数だとC++か。大分まえからそんな感じな気がw | link |
koizuka | 最近ひっぱりだしてないので規格書が手元にないな > C, C++ | link |
koizuka | PascalはBorland製品が快適でよく使っていたな。言語拡張しまくりだったけど。 | link |
_udonchan | プログラミング言語って抽象レイヤであるべきであって,コンパイラが云々,バイトコードが云々ってのを利用者が意識するのは,アセンブラかもしくはイージーなアセンブラであってプログラミング言語としてはどうかと思う.例えばC言語の関数は数学の関数のモデルである訳でバイトコードがどう | link |
_udonchan | まぁでもその辺は宗教論争なような気がする.インターンで出会ったスーパーハカーな人は,機械語に近いという理由でCが好きだと言っていたし. | link |
_udonchan | 正直なところ,Cはカーネル組むための言語だったという側面を考えると,只の抽象レイヤとして振る舞うだけではだめだよなあとは思うのだけど. | link |
suztomo | なんだこのゆうやりんによるCの関数の返り値が複数になりうるかというタイムライン。 | link |
yuyarin | C のどこが低次元だ! RT @suu_g : みんなC大好きだな、この低次元大好きなロリコンどもめ!! | link |
suztomo | あとでyuyarinがまとめ書いてくれるだろうからそれを読もう。あとranhaさんの張ってたサイクロンは「あとでよむ」 | link |
t_yano | あ、inlineってそういやC++だったか。 *P3 | link |
ranha | おおよそCだからで片付いてぱいちょんとかは型無いからむしろなんでも出来ないとおかしいし、型があって出来る言語は型があるなりにCと期限が違うしCでやりたいんだったらプリプロセサさんとかだと思うし、いちいちやるの嫌だなーだったらCycloneで良いんじゃ無いですかねみたいな | link |
DecimalBloat | Boost.LambdaやBoost.Bindを多用して異常に長くなったコンパイル時間をROに充てる | link |
DecimalBloat | tuple | link |
ranha | 標準のCを変えたいって言うんじゃなければ勝手に変えれば良いだろうし、勝手に変わってるのがさいくろんでそ http://bit.ly/g6dfT | link |
DecimalBloat | というような感じに展開されるようにプリプロセッサでごにょる | link |
DecimalBloat | ただの構文遊びだから全然役には立たない!それこそがfan! | link |
yugui | inline関数から小さな構造体を返した際には綺麗に最適化されて全部レジスタ直とかになる素敵な時代ですよ | link |
t_yano | 時代は進んでるなあ RT @yugui : inline関数から小さな構造体を返した際には綺麗に最適化されて全部レジスタ直とかになる素敵な時代ですよ *P3 | link |
t_yano | PascalでRecord渡すと参照渡しだったよなたしか。クラシックMacがPascalのAPIだったのでやったけど途中でCに移行したのでだいぶ忘れた。 *P3 | link |
yuyarin | おお,すてき! RT @yugui : inline関数から小さな構造体を返した際には綺麗に最適化されて全部レジスタ直とかになる素敵な時代ですよ | link |
suu_g | @yuyarin えっ? | link |
suu_g | @yuyarin えっ | link |
DecimalBloat | Cというのはつまり12だし、12とかまぁロリコンでしょう | link |
ranha | .@DecimalBloat 亜美真美はC!!!!!!!!!!!!!!!!????? | link |
Cryolite | この感じ……そろそろ C++ は12なのか13なのかについての篤い議論が始まる流れっ!!! | link |
ranha | すると高槻やよいさんはD | link |
yuzuhara | 構造体に共用体が入ってて不気味ってのはわからないなぁ... RT @nojiri_h : 構造体の中に共用体なんて不気味なものもあるんだよな、C言語って。 | link |
yuzuhara | Cとか高級言語だし | link |
Cryolite | この感じ……そろそろ C++ は12なのか13なのかについての篤い議論が始まる流れっ!!! | link |
DecimalBloat | 12なんだけど、第2期が始まると13になっているとかそういうあれ | link |
ranha | Cは亜美か真美で、C++は亜美真美です | link |
yuyarin | はい. RT @DecimalBloat : Cというのはつまり12だし、12とかまぁロリコンでしょう | link |
tokoroten | @yuyarin 業務が逆アセの俺に謝れwwwww 酔っ払いなう | link |
tgbt | @yuyarin スクリプターは年増好き? *Tw* | link |
pi8027 | @yuyarin <>で括るとパーサー的には一番楽な気がする。普通にコンマ区切りだとコンマ演算子との区別がむずい。 | link |
koizuka | 亜美真美やよい・・・!? Reading C言語の関数の多値返却は標準機能としてサポートできるか - yuyarinの日記 http://d.hatena.ne.jp/yuyarin/20090825/1251135887 | link |
pi8027 | むずいってかむりか。そういうのを特別扱いするとトリッキーな事しようとして爆死する人が出ますね。 | link |
iratqq | 多値ってcall/ccのことでしょ。 | link |
pi8027 | あと多値って変数に束縛しないと使えないのがなぁ。 | link |
pi8027 | @yuyarin 良く考えてみたらか{}でも大丈夫でした。 | link |
shamoshamo | @yuyarin して結論みたいなのはあるのかしら | link |
Omegamega | D言語使い「うっうー」 | link |
pi8027 | @yuyarin なぜ大丈夫かという話:{}は式の外でしか使われていない。は配列アクセスの演算子なのでarray[index]の形でしか使われていないので、複数の式をコンマ区切りにして括るだけなら全く違う形式としてパースさせられる。 | link |
yuyarin | 僕の結論は「機械語には落とせるので構文解析屋さん頑張ってください」です.あと引数に多値返却関数が使われた場合の挙動定義が必要でしょうか. RT @shamoshamo : @yuyarin して結論みたいなのはあるのかしら | link |
suztomo | @yuyarin おつかれさまです。 | link |
shamoshamo | @yuyarin だよね。実装は十分可能だよね。いろんな人がいろんな話題をふりまいててなかなかおもしろかった。ログ作成乙。 | link |
koizuka | C言語が12でD言語が13だとすると、C++言語は「右辺値をインクリメント」となるのでエラーです。 | link |
shachi | わろたw RT @koizuka : C言語が12でD言語が13だとすると、C++言語は「右辺値をインクリメント」となるのでエラーです。 | link |
yuyarin | [] と {} とだったら <> がよいねー RT @pi8027 : @yuyarin <>で括るとパーサー的には一番楽な気がする。普通にコンマ区切りだとコンマ演算子との区別がむずい。 | link |
pi8027 | @yuyarin | link |