2022-01-14 VBA 関数の仮引数はどうあるべきか。暗黙の型変換をどうやって回避するか

1
ほえほえ@スプシマン @hoehoe1234

型の判定は難しいですね。共通関数を作るとしてバリアントのvtの値(vartypeの値)を判定して数字を分類すると実質、整数と実数だけの分類でいいんでしょうか?それともやはりもっと詳しくバイト長なども判断がひつようなでしょうか?とりまつくってみます。 pic.twitter.com/oMqmSy1PN1

2022-01-18 13:37:16
拡大
ほえほえ@スプシマン @hoehoe1234

具象型仮引数では、VBAの暗黙の型変換のために、ゴミが入ったらゴミをだすことしかできないけど、バリアント型仮引数なら「ゴミが入ったら診断」をだすことができます。あまり用途はないでしょうが、実はバリアントのほうが堅牢なプログラミングができるのです。

2022-01-20 15:09:41
ほえほえ@スプシマン @hoehoe1234

他言語ではこのようなおかしなことは起こりませんので、このような対策は不要です。しかし、VBAではそういうことが起きますので堅牢な、かつ、診断を出すライブラリを作ろうとすれば仮引数は参照型バリアント型一択となるのです。これはライブラリを作ってみればわかります。

2022-01-20 15:11:12
ほえほえ@スプシマン @hoehoe1234

ただし、ここでもデフォルトプロパティについての考慮が要求されます。IS関数の殆どはデフォルトプロパティを起動するのです。この仕様は何も考えずに使うとSet構文の補完となりとてもうまく動くのですが、ライブラリでは困った状況になることがあります。 pic.twitter.com/SdILIQvKSN

2022-01-20 15:13:22
拡大
ほえほえ@スプシマン @hoehoe1234

VBAの基本的な挙動は「そうみなせるものはそう扱う」となっています。これはわかりにくいですが素晴らしいことだと思います。このようなトリックがなければ初心者が動くプログラムを作り始める難易度は相当高くなってしまうでしょう。もちろん、副作用もあります。この副作用を回避するには

2022-01-20 15:15:06
ほえほえ@スプシマン @hoehoe1234

TypeName関数とデフォルトプロパティの挙動、およびバリアントの実装などを知る必要があります。都度、うまくいくかどうか?を判断してもよいのですが、一度整理して判定関数群を作っておくと共通で使い回せて便利です。補足しおきますが、ほとんどメリットはありません。

2022-01-20 15:16:33
ほえほえ@スプシマン @hoehoe1234

これらはライブラリとかでトリックを使う場合に必要になります。もちろん、全て都度具象型で書くという選択もありますが、せっかくなのでライブラリにしてみましたというところです。 pic.twitter.com/moJP0QCpLJ

2022-01-20 15:20:55
拡大
ほえほえ@スプシマン @hoehoe1234

デフォルトプロパティの起動回避は、コード上でIsObjectをいれればよいだけなんだけど、それもなんだかしんどいし、逆にデフォルトプロパティ回避する体系にしておいて、起動したかったら()で囲んで引数渡して頂戴でもいいんだけど、理解されないだろうし、エレンがんとでもない。

2022-01-21 14:35:56
ほえほえ@スプシマン @hoehoe1234

ぼちぼち実装してるけど、これでやっと「関数にゴミが入ったら診断をだせる」ようになった。具象型仮引数ではゴミが入ったら暗黙の型変換により呼び出し関数には間違った、しかし、呼び出される関数からは正当にみえる値が引き渡されます。これは「ゴミが入ったらゴミがでる」状態です。 pic.twitter.com/PvHzqUNQvu

2022-01-21 14:42:28
拡大
ほえほえ@スプシマン @hoehoe1234

これを回避するには参照型仮引数で受けて、関数内で数字の種別を判定する必要があるのですが、これを「どう作成すればよいか?」を示すリファレンス実装を滝Libでしてみました。ここでもやはり問題になるのはデフォルトプロパティの挙動ですね。

2022-01-21 14:43:45
ほえほえ@スプシマン @hoehoe1234

依存関係の連鎖を切るために、このようなコードは大切。仮引数の既定値は参照不可が連鎖する場合に機能しない(具体的に参照不可が渡されると起動されない、構文的に省略する必要がある)のでコードで各階層でガードするひつようがあります。 pic.twitter.com/MSKYu4u4NW

2022-01-21 15:53:59
拡大
ほえほえ@スプシマン @hoehoe1234

関数の仮引数の既定値を使用している場合は潜在的な障害の可能性があります。あまり気にする必要なないですができれればコードでチェックするのが良いと思います。関数が次の A->B->C のようにあった場合、AからBを呼び出したときにBの仮引数ではIsMissingはTrueとなりますが、その引数を

2022-01-21 15:56:21
ほえほえ@スプシマン @hoehoe1234

そのままC関数にパスすると(このケースは多いですね)、C関数の仮引数の既定値設定機能は起動しません。なぜなら「参照不可変数」が実際に実引数として指定されているからです。なのでCでは既定値があっても参照不可変数が渡されます。

2022-01-21 15:57:39
ほえほえ@スプシマン @hoehoe1234

これは簡単なコードで回避できますので、回避したほうがよいように思います。とはいえ、あまり気にする必要もなくて、実行時に参照不可で止まったら調べればよいようなものだと思います。最初から堅牢なコードを書くのは難しいですから。

2022-01-21 15:59:00
ほえほえ@スプシマン @hoehoe1234

ノンプロのエクセルVBAは堅牢である必要はありません。どんどん止まっていいと思います。避けなければ行けないのは「間違ったまま動くこと」です。間違った値、仮引数でさも正しいように動いてしまうことです。これだけはなんとしても避けなければいけません。

2022-01-21 16:00:17
ほえほえ@スプシマン @hoehoe1234

そのためには、残念ですがVBAの暗黙の変換については多少の知識習得が必要になります。これはよいプログラムを作るためではなく、「まちがったまま動かないようにする」ためです。テスト、検証もその観点から作ります。止まるのは問題ありません。

2022-01-21 16:01:28
ほえほえ@スプシマン @hoehoe1234

dim x dim arr(1 to 2) x = arr(3) とすれば何が起きるか?そう、ゴミが入ったら診断が出るんですね。これがC言語だとわかりにくい障害につながります。すなわち「ゴミが入ったらゴミが出る」なんです。VBAで堅牢な共通関数を作ろうと思ったら使う側に配慮した「ゴミが入ったら診断が出る」ように

2022-01-24 13:49:09
ほえほえ@スプシマン @hoehoe1234

作るのが良いです。診断とは多くの場合は単に例外を投げるだけです。VBAにおいて具象型仮引数を使うと暗黙の型変換を回避出来ず、ゴミが入ったらゴミがでる関数しか作れません。より使用側に負担を掛ける、いわばC言語的な難しさが生まれちゃうんですよね。

2022-01-24 13:50:46
ほえほえ@スプシマン @hoehoe1234

アプリ側で精密な関数を作ることはあまり意味がありません。これは共通関数とかライブラリを作る場合の話です。こういった物を作るには細心の注意が必要でなのでアプリの関数に比べてライブラリの関数は設計、メンテナンスを含めて5倍程度の工数がかかります。

2022-01-24 13:52:00
ほえほえ@スプシマン @hoehoe1234

みなさんが普段享受しちている配列の範囲外アクセスなんかは「ゴミが入ったら診断が出る」の典型です。これと同じ仕様を提供できる唯一の方法が参照型バリアント仮引数です。診断を出したい場合はこの型による仮引数が必須事項となります。多くの場合はそこまでの必要はありませんが。

2022-01-24 13:54:11
ほえほえ@スプシマン @hoehoe1234

仮引数を具象型変数で受けることは暗黙の型変換を許容することであり、関数の精度の向上にはあまり関係がありません。一部の間違いを防げるだけです。呼び出し側のレベルを想定することは一般には正しくありません。ゴミが入ってくることを想定すべきです。しかし、バランスが難しいところでもあります

2022-01-24 13:56:24
ほえほえ@スプシマン @hoehoe1234

かといって闇雲に参照型バリアント仮引数をすすめるわけではありません。バリアントと参照型仮引数を理解して使いこなすには一定の知識が必要だからです。バランスが必要といったのはここです。多くの場合はそのレベルを要求できないからです。ですから必然的に共通関数とかライブラリに適用となります

2022-01-24 13:58:51
ほえほえ@スプシマン @hoehoe1234

参照型バリアント仮引数ではなくて、値型バリアント仮引数で良いのではないか?という疑問も湧いてきますが、そのとおりです。値型バリアント仮引数で問題ありません。いや、そちらのほうが望ましいでしょう。ではどういう違いがあるのでしょうか?

2022-01-25 01:54:43
ほえほえ@スプシマン @hoehoe1234

ByValを使うということは「コピーされる」ということです。実引数がバリアントでも具象型でもバリアント型仮引数には暗黙の型変換なく正しく値がコピーされます。ただし、配列の場合はコピーが発生します。

2022-01-25 01:56:03
ほえほえ@スプシマン @hoehoe1234

①値型バリアント仮引数(コピーが発生)   実引数 仮引数(バリアント) 1)値型  値型のコピー 2)参照型 参照型のコピー 3)配列  配列のコピー 4)バリアント バリアントのコピー 2)についてはコピーが作成されますが、指しているオブジェクトは同じです。

2022-01-25 02:00:07