Re[3]: virtual static и static virtual
От: Дмитро  
Дата: 21.07.03 13:35
Оценка:
Здравствуйте, Дмитро, Вы писали:

Д>Здравствуйте, deviv, Вы писали:


D>>2) нет транзитивности: как быть с такой цепочкой вызовов: обычный метод -> виртуальный статический->виртуальный статический? Оба статических вызова эквиваленты, но первый пойдет через виртуальную таблицу, второй напрямую.


Д>Черт подери!!! Это пока что самый убедительный довод против "статической виртуальности"!


Но я придумал workaround. Он заключается а том, чтобы неявно передавать адрес таблицы виртуальных методов с "статический виртуальный" метод.

По порядку. Итак, для начала введем такое понятие как класс-метод. Это понятие вводится в дополнение к обычному статическому методу.

Класс-метод похож на обычный статический метод тем, что он может быть вызван вне контекста какого-либо объекта и поэтому из него нельзя доступиться к нестатическим членам-данным.

Класс-метод отличается от обычного статического метода тем, что из него можно вызвать виртуальный класс-метод используя таблицу виртуальных методов. Он как бы "помнит" в контексте какого класса он был вызван. Фактически, в него неявно передается указатель на таблицу виртуальных методов.

Класс-методы могут быть как виртуальными, так и невиртуальными. Невиртуальные класс-методы не имеют соответствующей записи в таблице виртуальных методов и поэтому могут быть вызваны только напрямую. Виртуальные класс-методы могут быть вызваны и через таблицу виртеальных методов, и напрямую.

Класс-методы могут быть вызваны из обычных методов и из класс-методов, и не могут быть вызваны из статических методов без явного указания класса, в контексте которого они вызываются.

Из класс-методов можно вызывать класс-методы и статические методы, но нельзя вызывать обычные методы без явного указания объекта, к которому они применяются.

А теперь пример и возможный вариант трансляции.
struct A {
     int m_a;
     virtual void f_virtual_instance(int);
     virtual void f_virtual_instance2(int);
     void f_nonvirtual_instance(int);
     virtual __class void f_virtual_class(int);
     virtual __class void f_virtual_class2(int);
     __class void f_nonvirtual_class(int);
     static void f_static(int);
     void test_instance() { // демонстрация вызова класс-методов из обычного метода
          f_virtual_instance(1);
          A::f_virtual_instance(2);
          f_nonvirtual_instance(3);
          f_virtual_class(4);
          A::f_virtual_class(5);
          f_nonvirtual_class(6);
          f_static(7);
     }
     __class void test_class() { // демонстрация вызова класс-методов из класс-метода
          f_virtual_instance(1);
          A::f_virtual_instance(2);
          f_nonvirtual_instance(3);
          f_virtual_class(4);
          A::f_virtual_class(5);
          f_nonvirtual_class(6);
          f_static(7);
     }
     static void test_static() { // демонстрация вызова класс-методов из статического метода
          f_virtual_instance(1);
          A::f_virtual_instance(2);
          f_nonvirtual_instance(3);
          f_virtual_class(4);
          A::f_virtual_class(5);
          f_nonvirtual_class(6);
          f_static(7);
     }
};
struct B: A {
     int m_b;
     virtual void f_virtual_instance(int);
     void f_nonvirtual_instance(int);
     virtual __class void f_virtual_class(int);
     __class void f_nonvirtual_class(int);
     static void f_static(int);
};
void test_global() { // демонстрация взятия адреса класс-метода
     void (*func1)(int) = &A::f_static;
     void (*func2)(int) = &A::f_virtual_class;
}

struct A;
struct A__Vtbl { // объявление таблицы виртуальных методов класса A
     void f_virtual_instance(A *, int);
     void f_virtual_instance2(A *, int);
     void f_virtual_class(A__Vtbl *, int);
     void f_virtual_class2(A__Vtbl *, int);
};
struct A { // объявление класса A
     A__Vtbl const *pvtbl;
     int m_a;
};
void A__f_virtual_instance(A *, int);
void A__f_nonvirtual_instance(A *, int);
void A__f_virtual_class(A__Vtbl const *, int);
void A__f_nonvirtual_class(A__Vtbl const *, int);
void A__f_static(int);

A__Vtbl g_A__Vtbl = {
     A__f_virtual_instance,
     A__f_virtual_instance2,
     A__f_virtual_class,
     A__f_virtual_class2
};

// -------------------------

struct B;
struct B__Vtbl { // объявление таблицы виртуальных методов класса B
     void f_virtual_instance(B *, int);
     void f_virtual_instance2(B *, int);
     void f_virtual_class(B__Vtbl *, int);
     void f_virtual_class2(B__Vtbl *, int);
};
struct B { // объявление класса B
     B__Vtbl const *pvtbl;
     int m_a; // унаследованное член-данное
     int m_b; // собственное член-данное
};
void B__f_virtual_instance(B *, int);
void B__f_nonvirtual_instance(B *, int);
void B__f_virtual_class(B__Vtbl const *, int);
void B__f_nonvirtual_class(B__Vtbl const *, int);
void B__f_static(int);

B__Vtbl g_B__Vtbl = { // таблица виртуальных методов класса B
     B__f_virtual_instance,
     A__f_virtual_instance2, // унаследованный виртуальный обычный метод
     B__f_virtual_class,
     A__f_virtual_class2 // унаследованный виртуальный класс-метод
};

// -------------------------

void A__test_instance(A *this) { // демонстрация вызова класс-методов из обычного метода
// f_virtual_instance(1); -- вызов виртуального метода через таблицу виртуальных функций
     (this->pvtbl->f_virtual_instance)(this, 1);

// A::f_virtual_instance(2); -- вызов виртуального метода напрямую
     A__f_virtual_instance(this, 2);

// f_nonvirtual_instance(3); -- вызов невиртуального метода
     A__f_nonvirtual_instance(this, 3);

// f_virtual_class(4); -- вызов виртуального класс-метода через таблицу виртуальных функций
     (this->pvtbl->f_virtual_class)(this->pvtbl, 4);

// A::f_virtual_class(5); -- вызов виртуального класс-метода напрямую
     A__f_virtual_class(&g_A__Vtbl, 5);

// f_nonvirtual_class(6); -- вызов невиртуального класс-метода
     A__f_nonvirtual_class(this->pvtbl, 6);

// f_static(7); -- вызов статического метода
     A__f_static(7);
}

// -------------------------

void A__test_class(A__Vtbl const *pvtbl) { // демонстрация вызова класс-методов из класс-метода
// f_virtual_instance(1); -- ошибка компиляции: нельзя вызывать обычные методы
// без явного указания объекта, к которому они применяются

// A::f_virtual_instance(2); -- ошибка компиляции.

// f_nonvirtual_instance(3); -- ошибка компиляции.

// f_virtual_class(4); -- вызов виртуального класс-метода через таблицу виртуальных функций
     (pvtbl->f_virtual_class)(pvtbl, 4);

// A::f_virtual_class(5); -- вызов виртуального класс-метода напрямую
     A__f_virtual_class(&g_A__Vtbl, 5);

// f_nonvirtual_class(6); -- вызов невиртуального класс-метода
     A__f_nonvirtual_class(pvtbl, 4);

// f_static(7); -- вызов статического метода
     A__f_static(7);
}

// -------------------------

void test_static() { // демонстрация вызова класс-методов из статического метода
// f_virtual_instance(1); -- ошибка компиляции

// A::f_virtual_instance(2); -- ошибка компиляции

// f_nonvirtual_instance(3); -- ошибка компиляции

// f_virtual_class(4); -- ошибка компиляции: нет указания класса, в контексте которого выполнять вызов

// A::f_virtual_class(5); -- вызов класс-метода напрямую
     A__f_virtual_class(&g_A__Vtbl, 5);

// f_nonvirtual_class(6); -- ошибка компиляции.

// f_static(7);
     A__f_static(7);
}

// -------------------------

void A__f_virtual_class_stub0000(int param1) {
     A__f_virtual_class(&g_A__Vtbl, param1);
}

void test_global() { // демонстрация взятия адреса класс-метода
// void (*func1)(int) = &A::f_static;
     void (*func1)(int) = A__f_static;

// void (*func2)(int) = &A::f_virtual_class;
     void (*func2)(int) = A__f_virtual_class_stub0000;
}


Спасибо всем, кто одолел этот опус и дочитал до конца.
--
Дмитрий
Re[21]: Перпендикулярные свойства
От: Дмитро  
Дата: 21.07.03 13:54
Оценка:
Здравствуйте, _wqwa, Вы писали:

_>Здравствуйте, Дмитро, Вы писали:


_>Слушай, ты гладко и стройно рассуждаешь, но я все равно одного не понимаю.

_>Как определяется класс (динамический тип), для которого вызывается функция static virtual.

_>Если брать эти квалификаторы по отдельности -- тут вопросов нет:

_>Со static -- все понятно -- какой класс в префиксе вызова указали (или какой статический тип имеет экземпляр класса или указатель, через который вызвали), для такого и вызывается.
_>С virtual -- тоже понятно. Берем указатель на экземпляр, смотрим его действительный класс (динамик-тайп) и для этого класса и вызываем функцию.

_>А если у нас нету экземпляра, откуда мы знаем, какую из множества одноименных функций-членов классов, принадлежащих одному генеалогическому дереву, сейчас надо вызвать?


Если указан класс в префиксе -- вызываем метод того класса, какой указан.
Если класс не указан, но вызов производится из обычного метода -- адрес метода выбираем из таблицы виртуальных методов.
Если класс не указан, но вызов производится из внешней функции -- ошибка компиляции (как и при вызове обычного статического метода)

Вообще-то проблема неожиданно возникла при вызове "статического виртуального" метода из "статического виртуального", либо просто статического. Re: virtual static и static virtual (by deviv)
Автор: deviv
Дата: 21.07.03

При этом в той схеме, которой я указал ранее, терялась информация о типе и вложенный вызов "статического виртуального" уже не мог быть сделан правильно.
--
Дмитрий
Re[12]: Перпендикулярные свойства
От: Павел Кузнецов  
Дата: 21.07.03 15:27
Оценка:
Здравствуйте, folk, Вы писали:

f>
f> struct Abstract
f> {
f>   static virtual const char* typeName() = 0;
f> };

f> struct Foo : public Abstract
f> {
f>   static virtual const char* typeName()
f>     {return "Foo";}
f> };

f> struct Bar : public Abstract
f> {
f>   static virtual const char* typeName()
f>     {return "Bar";}
f> };
f>


f> поскольку этой функции не передается неявный параметр this, то

f> где-нибудь в шаблоне можем написать так: typename T::typeName()

Но, без объекта, эта штуковина вернет результат вызова функции, соответствующей
статическому типу T, т.к. без объекта не сможет добраться до vtbl.
И зачем оно такое, загадочное, нужно?..
Posted via RSDN NNTP Server 1.6 RC1
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[13]: Перпендикулярные свойства
От: folk Россия  
Дата: 21.07.03 21:55
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Здравствуйте, folk, Вы писали:


f>>
f>> struct Abstract
f>> {
f>>   static virtual const char* typeName() = 0;
f>> };

f>> struct Foo : public Abstract
f>> {
f>>   static virtual const char* typeName()
f>>     {return "Foo";}
f>> };

f>> struct Bar : public Abstract
f>> {
f>>   static virtual const char* typeName()
f>>     {return "Bar";}
f>> };
f>>


f>> поскольку этой функции не передается неявный параметр this, то

f>> где-нибудь в шаблоне можем написать так: typename T::typeName()

ПК>Но, без объекта, эта штуковина вернет результат вызова функции, соответствующей

ПК>статическому типу T, т.к. без объекта не сможет добраться до vtbl.

Ну разумеется.

ПК>И зачем оно такое, загадочное, нужно?..


Оно не намного загадочнее вызова константного или неконстантного метода в зависимости от константности this. ИМХО.

Если говорить об этом примере, то статический вызов может использоваться в шаблоне, создающем экземпляры, или в шаблоне, выводящем статистическую информащию (например кол-во экземпляров). В обоих этих случаях тип известен, а экземпляр не нужен.
На самом деле, люди не читают газеты, они принимают их каждое утро, так же как ванну. ©Маршалл Мак-Льюэн
Re[22]: Перпендикулярные свойства
От: _wqwa США  
Дата: 22.07.03 05:46
Оценка:
Здравствуйте, Дмитро, Вы писали:



Д>Если указан класс в префиксе -- вызываем метод того класса, какой указан.

Д>Если класс не указан, но вызов производится из обычного метода -- адрес метода выбираем из таблицы виртуальных методов.
Д>Если класс не указан, но вызов производится из внешней функции -- ошибка компиляции (как и при вызове обычного статического метода)

Олрайт, теперь понял. Правда, большого смысла в этом не вижу... Ну да это уже мое личное мнение
Кто здесь?!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.