私は現在プログラミング言語を設計しており、この問題を解決する方法に興味があります:
次のようなクラス (またはインターフェイス) A があるとします。
class A { // size is 4 bytes
int32 a = 0;
}
それを拡張し、次のように見える2番目のクラス B:
class B extends A { // size is 8 bytes
int32 b = 0;
}
そして、次のような関数 f があるとします。
int32 f(A first, A second) {
return first.a + second.a;
}
ただし、2 つの B で呼び出すと、second.a は 2 つの As で呼び出された場合と同じ場所にはなりません。これは、最初のパラメーターがそれをシフトするためです。これを解決するための私の現在の考えは次のとおりです。
- 不明なサイズのパラメーターを許可せず、ポインターまたは参照として渡すように強制します (これが Rust の機能だと思います)。
- 次のすべての情報をコール スタックに書き込みます。
- first と second の可能なサイズごとに関数を作成し、既知の場合はコンパイル時、または vtables を使用して実行時にどちらを呼び出すかを決定します。
2 番目のアイデアは、サブタイプを使用してほとんどまたはまったく呼び出されない場合でも、すべての関数でサポートされる必要があるため、問題になります。これは非効率的です。
3 番目のアイデアでは、多くの関数を作成する必要があります (20 の異なるサブタイプを持つことができる 5 つのパラメーターを受け入れる関数は、未知の型のパラメーターで 1 つだけ呼び出された場合、100 個の同様のコードを生成する必要があります)。それを使用する関数が 1 つだけあるクラスごとに vtable が必要になります。また、コンパイル済みのライブラリ内の関数は、新しいサブタイプでは使用できませんでした。
2 と 3 を組み合わせて、同じ関数の 2 つのバージョンを作成します。1 つは型のみを受け入れ、もう 1 つはサブタイプも受け入れるものです。これらの問題のいくつかは解決できます。
これに対するより良い解決策があるかどうか、および C++ などの他の言語がこれをどのように実装しているかに興味があります。