Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:01
Оценка: 36 (2) :)
Есть вот такой метод:
void X::foo()
{
    void* p = this;
}


Компилятор студии (2005/2008) в дебаг режиме генерирует для него вот такой код:
    void* p = this;
  mov         eax,dword ptr [this] 
  sub         eax,4 
  mov         dword ptr [p],eax


Вопрос: откуда взялось смещение this на 4 при конвертации в void*?


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Вопрос на засыпку
От: Аноним  
Дата: 25.08.09 09:04
Оценка:
Здравствуйте, remark, Вы писали:

R>Есть вот такой метод:

R>
R>void X::foo()
R>{
R>    void* p = this;
R>}
R>


X — полиморф?
Re: Вопрос на засыпку
От: Аноним  
Дата: 25.08.09 09:06
Оценка:
А не указатель ли это на таблицу виртуальных функций? Попробуй это же для класса без вируальных функций.
Re[2]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:07
Оценка:
Здравствуйте, Аноним, Вы писали:

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


R>>Есть вот такой метод:

R>>
R>>void X::foo()
R>>{
R>>    void* p = this;
R>>}
R>>


А>X — полиморф?


Я думаю, что через 5 минут после публикации вопроса нецелесообразно давать какие-либо подсказки, т.к. это будет нечестно по отношению к тем, кто хочет выдать свой ответ без каких-либо подсказок.

1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:11
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А не указатель ли это на таблицу виртуальных функций? Попробуй это же для класса без вируальных функций.


Поясню: я ответ знаю
Вопрос для тех, кто хочет проверить и посоревноваться в знании С++, и возможно узнать новую занятную деталь.

1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Вопрос на засыпку
От: R1K0 Россия  
Дата: 25.08.09 09:14
Оценка:
Честно скажу, что ответ не знаю.
Но размер 4 навевает мысли о теме адреса указателя...
Я хоть на правильном пути или как ?
Re[4]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:19
Оценка:
Здравствуйте, R1K0, Вы писали:

RK>Честно скажу, что ответ не знаю.

RK>Но размер 4 навевает мысли о теме адреса указателя...
RK>Я хоть на правильном пути или как ?

Я пока не буду давать никаких подсказок. Это будет не честно по отношению к тем, кто хочет решить без подсказок.
Если до завтра не будет ответа, то начну давать подсказки.

1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Вопрос на засыпку
От: Аноним  
Дата: 25.08.09 09:35
Оценка: 42 (1) +1
R>Вопрос для тех, кто хочет проверить и посоревноваться в знании С++, и возможно узнать новую занятную деталь.

Может этот класс унаследован от нескольких, и эта функция второго базового, поэтому ей передается смещенный this, а при приведении к void* получаем указатель на "полный" объект.

А важно вообще, что это дебажный режим вижуалстудии? Может вижуалстудия что-то вспомогательное хранит, и непосредственно к с++ это отношения не имеет? Информации очень мало, и так угадывать — пальцем в небо.
Re: Вопрос на засыпку
От: Bell Россия  
Дата: 25.08.09 09:37
Оценка: 42 (1)
Здравствуйте, remark, Вы писали:

Дело в 4.10/2, а пример вот он:

struct Y1 
{
   virtual void foo() {}
};

struct X : virtual Y1
{
   void foo();
};

void X::foo()
{
   void* p = this;
}
Любите книгу — источник знаний (с) М.Горький
Re[4]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:44
Оценка:
Здравствуйте, Аноним, Вы писали:

R>>Вопрос для тех, кто хочет проверить и посоревноваться в знании С++, и возможно узнать новую занятную деталь.


А>Может этот класс унаследован от нескольких, и эта функция второго базового, поэтому ей передается смещенный this, а при приведении к void* получаем указатель на "полный" объект.


Засчитывается.
Вначале я хотел попросить пример кода. Но т.к. Bell уже привёл, смысла большого нет.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:46
Оценка:
Здравствуйте, Bell, Вы писали:

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


B>Дело в 4.10/2, а пример вот он:


Засчитывается.
Хотя я имел виду следующий код, суть одна и так же:

struct A
{
    virtual void foo() {}
};

struct B
{
    virtual void bar() {}
};

struct X : B, A
{
    void foo();
};

void X::foo()
{
    void* p = this;
}



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 09:51
Оценка:
Здравствуйте, remark, Вы писали:

R>Вопрос: откуда взялось смещение this на 4 при конвертации в void*?


Занятный момент то, что статический тип 'this' есть X*. Хотя физически значение 'this' — указатель на другой объект Y. Соотв. компилятор при *любом* использовании 'this' "устраняет" это досадное недоразумение смещением значения на некоторую величину. Значение этой величины не обязательно sizeof(void*), а может быть любым.
Соотв. это проявляется и в таком коде:
void X::foo()
{
   uintptr_t p = (uintptr_t)this;
   //mov         eax,dword ptr [this] 
   //sub         eax,4 
   //mov         dword ptr [p],eax 
}




1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Вопрос на засыпку
От: Кодт Россия  
Дата: 25.08.09 09:57
Оценка:
Здравствуйте, Bell, Вы писали:

B>Дело в 4.10/2, а пример вот он:


А можно пояснить? И кстати, натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига.
ЧЯДНТ?
#include <iostream>
using namespace std;

struct X { virtual void foo() {} };
struct Z { int x; virtual ~Z() {} }; // пытался сдвинуть базу

struct Y : Z, X {
    void foo()
    {
        char* c = (char*)this; // на всякий случай, чтобы оббежать прямой кастинг Y* -> void*
        cout << this << " " << (void*)this << " " << (void*)c << endl;
        // 0x22cc50 0x22cc50 0x22cc50 -- адрес объекта Y целиком
    }
};

int main()
{
    Y y; y.foo();
    cout << &y << " " << &(X&)y << endl;
    // 0x22cc50 0x22cc58 -- адрес объекта Y и его базы X
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[3]: Вопрос на засыпку
От: Аноним  
Дата: 25.08.09 10:01
Оценка:
К>натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига.

А если вместо c-style cast сделать dynamic_cast<void*>, что получится?

P.S. Попробовать самому сейчас нет возможности.
Re[2]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 10:02
Оценка:
Здравствуйте, Bell, Вы писали:

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


B>Дело в 4.10/2, а пример вот он:


Честно говоря, я немного затрудняюсь с ходу трактовать 4.10/2

4.10/2
An rvalue of type “pointer to cv T ,” where T is an effective object type, can be converted to an rvalue of
type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points
to the start of the storage location where the object of type T resides, as if the object is a most derived
object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null
pointer value of the destination type.


Разве указатель на объект как на MDT и как на подобъект другого объекта может различаться в принципе?

Я это смещение объяснил для себя несколько по-другому: как артефакт реализации виртуальных функций получаем, что в некоторой функции статический тип this есть T*, а реальное значение this получается указатель на какой-то другой объект Y*. Соотв. компилятору в реализации приходится это учитывать и смещать this при каждом использовании.
Следствие №1: это не регламентируется стандартом, т.к. реализация виртуальных функций не регламентируется.
Следствие №2: это проявляется не только при конвертации к void*, а при *любом* использовании. Например при конвертации к uintptr_t.
Следствие №3: вполне можно представить реализацию виртуальных функций, где статический и динамический типы this всегда будут совпадать, соотв. там не будет таких артефактов в принципе. Для этого, например, можно в таблице виртуальных функций вместе с каждым указателем хранить смещение от базового типа (где впервые объявлена виртуальная функция) до реального типа (в котором переопределена виртуальная функция). Тогда в месте вызова указатель будет соотв. смещаться. Хотя практически это наверное менее эффективно, чем делать это смещение уже внутри реальной функции.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 10:05
Оценка:
Здравствуйте, Кодт, Вы писали:

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


B>>Дело в 4.10/2, а пример вот он:


К>А можно пояснить? И кстати, натурные эксперименты с VC 2005 и gcc 3.4.4 не дали желанного сдвига.

К>struct Y : Z, X {
К> void foo()
К> {
К> char* c = (char*)this; // на всякий случай, чтобы оббежать прямой кастинг Y* -> void*
К> cout << this << " " << (void*)this << " " << (void*)c << endl;
К> // 0x22cc50 0x22cc50 0x22cc50 -- адрес объекта Y целиком
К> }
К>};
К>[/ccode]


Всё правильно. Компилятор смещает this при *любом* использовании. Т.к. расхождение статического и динамического типа this — есть не более чем артефакт реализации виртуальных функций.
См мой комментарий ниже:
http://www.rsdn.ru/forum/cpp/3514848.1.aspx
Автор: remark
Дата: 25.08.09



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Вопрос на засыпку
От: Кодт Россия  
Дата: 25.08.09 10:09
Оценка: 48 (2)
К>ЧЯДНТ?

Я туплю, вот что я делаю не так.

На стеке ещё валяется this от базы (сдвинутый), а Студия срезает угол: вместо того, чтобы сразу восстановить до текущего типа, она предпочитает восстанавливать в местах использования.

Окей, тогда вот такой код
struct X { virtual void* f()=0; };
struct Y { virtual void* f()=0; virtual void* g()=0; };

struct Z : X, Y
{
    virtual void* f() { return this; } // манёвры совершаются в функции-переходнике [thunk]:Z::f`adjustor{4}
    virtual void* g() { return this; } // совершаем манёвры прямо в теле функции
};

int main()
{
    Z z;
    Y* y = &z;
    return y->f() == y->g();
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[4]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 10:24
Оценка:
Здравствуйте, Кодт, Вы писали:

К>На стеке ещё валяется this от базы (сдвинутый), а Студия срезает угол: вместо того, чтобы сразу восстановить до текущего типа, она предпочитает восстанавливать в местах использования.


Я думаю, что это связано исключительно с дебаг генерацией. В релиз логично смещать в начале функции. Хотя возможно по-месту всё равно будет эффективнее, если например мы имели mov eax, [ecx+20h], заменяем его просто на mov eax, [ecx+40h]. И никаких накладных расходов вообще.


К>Окей, тогда вот такой код


Занятный пример. Честно говоря, я не помню, что бы когда-либо сталкивался с adjustor служебной функцией.
Наверное adjustor всё равно эффективнее смещения this в месте вызова.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 10:26
Оценка: 12 (1)
Здравствуйте, remark, Вы писали:

R>>Вопрос: откуда взялось смещение this на 4 при конвертации в void*?


R>Занятный момент то, что статический тип 'this' есть X*. Хотя физически значение 'this' — указатель на другой объект Y. Соотв. компилятор при *любом* использовании 'this' "устраняет" это досадное недоразумение смещением значения на некоторую величину. Значение этой величины не обязательно sizeof(void*), а может быть любым.


Вообще изначально вопрос возник при разбирательстве, почему в следующем коде equal получается равным true. Это было бы логично, если бы статические типы this и other различались, ну там приведение между базовым и производным типами. А тут получается, что статические типы одинаковые, однако 2 физических не равных указателя при сравнении дают true.

void T::f()
{
  T* other = ...;
  // assume this = 0x1000, and other = 0x2000
  bool equal = (this == other);
  ...
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: Вопрос на засыпку
От: remark Россия http://www.1024cores.net/
Дата: 25.08.09 10:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Окей, тогда вот такой код

К>
К>struct X { virtual void* f()=0; };
К>struct Y { virtual void* f()=0; virtual void* g()=0; };

К>struct Z : X, Y
К>{
К>    virtual void* f() { return this; } // манёвры совершаются в функции-переходнике [thunk]:Z::f`adjustor{4}
К>    virtual void* g() { return this; } // совершаем манёвры прямо в теле функции
К>};

К>int main()
К>{
К>    Z z;
К>    Y* y = &z;
К>    return y->f() == y->g();
К>}
К>



Как ты думаешь, это имеет какое-то отношение к 4.10/2:
http://www.rsdn.ru/forum/cpp/3514848.1.aspx
Автор: remark
Дата: 25.08.09

?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.