Re[3]: детские грабли: declspec novtable
От: const_volatile  
Дата: 10.10.14 14:23
Оценка: +4 :)
Здравствуйте, Кодт, Вы писали:

К>>>Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ?

PD>>Сказать, что детям играть со спичками declspec(novtable) не разрешается.
К>Должен, но не говорит.
К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.
К>Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.

так оно для этого и предназначено. это костыль для библиотек, создающих, например, COM-объекты. посмотри, как объявляются COM-классы в windows SDK. а вообще, смешной топик: взять дробовик, зарядить его картечью, тщательно прицелиться себе в ногу, нажать на спуск, а потом удивляться, почему это тебе ногу расхерачило.
Re: детские грабли: declspec novtable
От: Pavel Dvorkin Россия  
Дата: 10.10.14 13:24
Оценка: +2
Здравствуйте, Кодт, Вы писали:

К>Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ?


Сказать, что детям играть со спичками declspec(novtable) не разрешается.
With best regards
Pavel Dvorkin
Re[8]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 19:00
Оценка: 1 (1)
Здравствуйте, Alexander G, Вы писали:

AG>А может, чтобы не генерировать (и, соответственно, не вызывать) конструктор совсем?


Ну и это тоже.
Правда, такой конструктор (определяемый компилятором) отлично инлайнится.
В дебаге он должен делать одно бестолковое действие — присвоить vptr'у значение, которое тут же будет переприсвоено.
В релизе это вообще выродится.

Теоретические проблемы могут возникнуть, если интерфейс объявлен в нескольких модулях. Поскольку у него нет спецификации dllexport/dllimport, то, формально, мы получим нарушение ODR — несколько разных экземпляров vtable (хотя и идентичных). Ну и пёс с ними, на самом деле.

Ещё одна закавыка, если включено RTTI. Всякая служебная информация о типе находится тоже в vtable под правильными отрицательными смещениями.
Если несколько vtable разных, то может накрениться dynamic_cast. Если таблицы вообще не созданы, программа может непредсказуемо рухнуть.
Ибо нефиг смешивать RTTI и COM.
Перекуём баги на фичи!
Re[2]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 13:35
Оценка: +1
Здравствуйте, rus blood, Вы писали:

RB>If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).


Не AV, а любое другое UB.

#include <stdio.h>

struct __declspec(novtable) A
{
    A() { printf("%p A::ctor\n", this); }
    ~A() { printf("%p A::dtor\n", this); }

    virtual void foo() { printf("%p A::foo\n", this); }
    void bar() { A::foo(); }
    void buz() { foo(); }
};

struct B
{
    virtual void hello() { printf("%p B::hello\n", this); }
};

typedef int (*FUN)();
FUN pvfc_table[1] = { _purecall };

int surprize() { printf("surprize!\n"); return 0; }
FUN surprize_table[1] = { surprize };

template<FUN* table>
struct С
{
    С() : p(table) {}
    FUN* p;
};

void runA()
{
    A a;
    a.foo();
    a.bar();
    a.buz();
}

template<class T>
void runT()
{
    T t;
}

int main()
{
    __try
    {
        // выберите любой вариант
        runT< B >();
        runT< C<nullptr> >();
        runT< C<pvfc_table> >();
        runT< C<surprize_table> >();
        runT< C<(FUN*)0xDEADFACE> >();

        runA();
    }
    __except(1)
    {
        printf("catastropha!\n");
    }
}


Фокус в том, что наш объект A вообще не инициализирует vptr — и хватает любой мусор, лежавший там до конструирования.
Перекуём баги на фичи!
Отредактировано 10.10.2014 13:36 Кодт . Предыдущая версия .
Re[4]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 14:34
Оценка: -1
Здравствуйте, const_volatile, Вы писали:

_>так оно для этого и предназначено. это костыль для библиотек, создающих, например, COM-объекты. посмотри, как объявляются COM-классы в windows SDK. а вообще, смешной топик: взять дробовик, зарядить его картечью, тщательно прицелиться себе в ногу, нажать на спуск, а потом удивляться, почему это тебе ногу расхерачило.


Не совсем так. Этот костыль должен работать в паре с объявлением функций чисто виртуальными.
Его главное назначение — отвадить компилятор создавать ненужные таблицы, а линкер, соответственно, — искать эти таблицы.
(Действительно: кому нужна таблица, в которой все элементы — _purecall ?)
Перекуём баги на фичи!
детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 13:07
Оценка:
Навеяно http://habrahabr.ru/company/pvs-studio/blog/239915

Пользователи MSVC, знайте, что здесь водятся львы.
#include <stdio.h>

struct __declspec(novtable) A
{
    virtual void foo() { printf("%p A::foo\n", this); }
    void bar() { A::foo(); }
    void buz() { foo(); }
};

int main()
{
    __try
    {
        A a;
        a.foo();
        a.bar();
        a.buz();
    }
    __except(1)
    {
        printf("catastropha!\n");
    }
}

Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ?
Перекуём баги на фичи!
Re: детские грабли: declspec novtable
От: rus blood Россия  
Дата: 10.10.14 13:10
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Пользователи MSVC, знайте, что здесь водятся львы.


[msdn]
If you attempt to instantiate a class marked with novtable and then access a class member, you will receive an access violation (AV).
[/msdn]
Имею скафандр — готов путешествовать!
Re[2]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 13:39
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

К>>Что должен сказать компилятор , и что должна сделать программа, если она скомпилируется ?

PD>Сказать, что детям играть со спичками declspec(novtable) не разрешается.

Должен, но не говорит.
По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.
Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.
Перекуём баги на фичи!
Re[3]: детские грабли: declspec novtable
От: Хон Гиль Дон Россия  
Дата: 10.10.14 14:07
Оценка:
Здравствуйте, Кодт, Вы писали:


К>Должен, но не говорит.

К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.
К>Но, так как это расширение языка, т.е. заплатка поверх парсера, то никакой диагностики мы не получаем — и можем смело создавать экземпляры фактически абстрактного класса.

Это ж для MS считай традиционный баг. Помниться, в VC 6 можно было создавать анонимные экземпляры абстрактных классов. С учетом того, что их можно было передавать даже по неконстантной ссылке, было прикольно. Да и потом что-то такое кажется всплывало.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: детские грабли: declspec novtable
От: uzhas Ниоткуда  
Дата: 10.10.14 14:58
Оценка:
Здравствуйте, Кодт, Вы писали:

К>(Действительно: кому нужна таблица, в которой все элементы — _purecall ?)


кому она мешает и почему?
Re[6]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 15:32
Оценка:
Здравствуйте, uzhas, Вы писали:

К>>(Действительно: кому нужна таблица, в которой все элементы — _purecall ?)

U>кому она мешает и почему?

Линкеру?
Занимает место в секции констант.
Перекуём баги на фичи!
Re[7]: детские грабли: declspec novtable
От: uzhas Ниоткуда  
Дата: 10.10.14 15:57
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Линкеру?

линкеру не мешает. мешает, видимо, тому, кто ждет линкер и надеется получить некий профит по времени сборки или размеру бинаря

К>Занимает место в секции констант.

я так понимаю, что для тебя это очень критично (кстати, почему?). мне это не критично (ну пока я не знаю, почему я должен этого бояться) и стандарто-писателям тоже

ты строковые константы не используешь в коде ради экономии места в секции констант? считываешь их из файла или сети, когда надо что-то напечатать на экран или через beep азбукой морзе что-то сообщаешь?
Re[8]: детские грабли: declspec novtable
От: Кодт Россия  
Дата: 10.10.14 16:19
Оценка:
Здравствуйте, uzhas, Вы писали:

U>я так понимаю, что для тебя это очень критично (кстати, почему?).


Для меня это совершенно некритично.
Просто я прочитал статью на хабре, проверил — и схватился за голову: какая шикарная грабля.
Главное, что авторам PVS Studio пишут претензии: мол, почему вы ругаетесь на memcpy не-POD-ов, если там явно сказано __declspec(novtable).
Значит, кто-то ведь этим пользуется. А на самом деле, это пенопластовый бронежилет, с пожизненной гарантией: как только прострелили, так гарантия кончилась.
Перекуём баги на фичи!
Re[7]: детские грабли: declspec novtable
От: Alexander G Украина  
Дата: 10.10.14 18:48
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Линкеру?

К>Занимает место в секции констант.

А может, чтобы не генерировать (и, соответственно, не вызывать) конструктор совсем?

(это можно, если у novtable класса нет данных, а базы, если есть, такие же novtable без данных)
Русский военный корабль идёт ко дну!
Re[3]: детские грабли: declspec novtable
От: vdimas Россия  
Дата: 13.07.15 23:01
Оценка:
Здравствуйте, Кодт, Вы писали:

К>По сути, __declspec(novtable) автоматически делает все виртуальные функции чисто виртуальными.


Он убирает генерацию vtable. Полезно для базовых классов. Это не то же, что чисто-виртуальные ф-ии. В наследнике эти ф-ии унаследуются со всем своим не чисто-виртуальным телом.
Отредактировано 13.07.2015 23:04 vdimas . Предыдущая версия .
Re[5]: детские грабли: declspec novtable
От: vdimas Россия  
Дата: 13.07.15 23:04
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Не совсем так. Этот костыль должен работать в паре с объявлением функций чисто виртуальными.


Необязательно. При единичном наследовании в конструкторе наследника все-равно затирается vptr базы, т.е. таблица базы просто не нужна. А не чисто виртуальные ф-ии от базы — нужны.
Re[9]: детские грабли: declspec novtable
От: vdimas Россия  
Дата: 13.07.15 23:17
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Теоретические проблемы могут возникнуть, если интерфейс объявлен в нескольких модулях. Поскольку у него нет спецификации dllexport/dllimport, то, формально, мы получим нарушение ODR — несколько разных экземпляров vtable (хотя и идентичных).


Не должно. В бинарнике должен быть vtable только если один из модулей бинарника ссылается на конструктор типа с vtable.


К>Ещё одна закавыка, если включено RTTI. Всякая служебная информация о типе находится тоже в vtable под правильными отрицательными смещениями.


Если экземпляры базового типа не создаются, то никаких проблем.


К>Если несколько vtable разных, то может накрениться dynamic_cast.


Он и накреняется именно в виндах. Чудес не бывает. ))
Экземпляры, созданные модулями из разных DLL, обычно "не видят" друг друга. Да и как, собсно? В Linux эта проблема решается уровнем видимости символов — по умолчанию линкер не грузит повторяющиеся символы из других модулей, поэтому на всех получается одна vtable и общий RTTI. В виндах — увы, увы.

К>Если таблицы вообще не созданы, программа может непредсказуемо рухнуть.


Каким образом? Созданные таблицы связываются в цепочки. Если ты получил экземпляр объекта-наследника, созданного в другом модуле, т.е. у него есть живая ссылка на его vtable, ты будешь бегать по вполне живым таблицам rtti из того модуля. Не найдешь, правда, ничего... но ничего не рухнет.

К>Ибо нефиг смешивать RTTI и COM.


Легко ))
Толку, правда, мало.

======
В плагинных RTTI-based фреймворках обычно есть некая общая DLL, из которой экспортируются нужные интерфейсы. Хост и плагины ссылаются на эту DLL, поэтому RTTI вокруг определённых в ней интерфейсов вполне пашет.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.