で
Size: a a a
で
DM
N
M
M
DM
M

DI
PM
Б
const someFunc = (obj: { a: number }) => {
obj.a += 1;
};
но передавать мы можем объекты где помимо этого поля могут быть разнообразные сочетания других полейsomeFunc({a: 1, b: 1})
someFunc({x: 1, a: 1, z: 1})
И я вспомнил что эта проблема похожа на проблему множественного наследования и появился вопрос - кто-нибудь знает как с++ компиляторы понимают по какому оффсету расположено поле с таким-то именем в случае множественного наследования?class A {
int a;
}
class B {
int b;
}
...//еще 22 класса
class Y {
int y;
}
class Z {
int z
}
И есть функция которая объявляет что в качестве параметра принимает объекты с полем "k"void someFunc(K& obj){
obj.k += 1;
}
То есть к этой функции можно передавать объекты классов которые наследуются от класса KТут можно заметить что поле "к" находится по первому оффсету, а если нет то мы отсортируем в общем сделаем так чтобы оффсеты полей которые принимает функция будут находиться в начале объекта. Ок, это будет работать в случае если у нас есть только одна такая функция, но что если функций много и каждой нужно знать оффсет поля со своим именем или даже несколько таких полей. Тут уже не получится сделать так чтобы все поля с таким именем лежали по одинаковым оффсетам потому что сочетания полей (базовых класов) могут быть самые разные
class KAB: K, A, B {} //{k: 0, a: 0, b: 0}
class KAO: K, A, T {} //{k: 0, a: 0, t: 0}
class KNJ: K, A, T {} //{k: 0, n: 0, j: 0}
//много других сочетаний
someFunc(new KAB())
someFunc(new KAO())
someFunc(new KNJ())
void someFunc(K& obj){
obj.k += 1;
}
void someFunc2(T& obj){
obj.k += 1;
}
auto obj = new KOT();
someFunc(obj)
someFunc2(obj)
auto obj = new AKT();
someFunc(obj)
someFunc2(obj)
Поверхностное гугление не дало ответов на вопросы как компилятор с++ вычисляет оффсеты для таких различных сочетаний базовых классов. Есть пейпер от Страуструпа https://www.usenix.org/legacy/publications/compsystems/1989/fall_stroustrup.pdf но там ничего не написано про такие сочетания, есть общие фразы вроде ": the compiler knows the location in the object of eachAC
SK
fieldSymbolOffset + classId будет лежать индекс поля в классе. Таблица такая в общем случае будет разряженная, ее можно сжимать.Б
{a, b}
{a, b, c}
{b, c, d}
{a, x, y}
{b, someField}
{someField, someField2}
Все эти литералы можно представить как класс который наследует от некоторого количества базовых классов (н-классов по одному для каждого уникального поля)class Base1 { a }
class Base2 { b }
class Base3 { c }
class Base4 { d }
class Base5 { x }
class Base6 { y }
class Base7 { someField }
class Base8 { someField2 }
и тогда любой литеральный объект можно представить как наследование нескольких базовых классовconst obj = {a, x, y} -> new class extends Base1, Base5, Base6
const obj2 = {b, someField} -> new class extends Base2, Base7
и тогда задача вычисления оффсета поля для объекта который имеет такой-то интерфейс (имеет поле c таким то именем)function someFunction(obj: {someField}) {
obj.someField += 1;
}
someFunction({b, someField})
someFunction({someField, someField2, a})
someFunction({a, someField2, b, c})
сводится к проблеме вычисления оффсетов при множественном наследованииfunction someFunction(obj: Base7) {
obj.someField += 1;
}
someFunction(new class extends Base2, Base7)
someFunction(new class extends Base7, Base, Base1)
someFunction(new class extends Base1, Base7, Base2, Base3)
и я вот хочу понять как в c++ компилятор понимает по какому оффсету нужно загрузить поле внутри функции которая принимает некий базовый класс а передаем мы объекты классов которые наследуются от нескольких базовых классов (может образоваться комбинаторное количество сочетаний базовых классов)Б
принимает указатель и ожидает там увидеть данные и vtable по одним и тем же смещениям для каждого конкретного класса — просто передаём ей указатель на нужный кусок (заголовка) объекта
К
AC
AC
Б
AC