Допустим есть класс экземпляр которого возвращает функция реализованная в dll.
И dll собрана компилятором А.
class Foo {
...
};
Foo* CreateFoo();
Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный?
Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
Re: Представление класса в памяти разными компиляторами
Здравствуйте, fanruten, Вы писали:
F>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный? F>Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
Можно проверить поддерживает ли данный компилятор COM или нечто аналогичное.
Если "да" то в принципе можно делать предположения об однообразии. А лучше прямо COM интерфейсы и писать.
F>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный? F>Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
Для таких гарантий должно совпадать соглашение о вызове методов. Дэйаут класса совпадать для этого не должен...
Правда нужно проследить, чтобы не было inline-подстановок и прямых обращений к полям и базам.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Представление класса в памяти разными компиляторами
Здравствуйте, fanruten, Вы писали:
F>Допустим есть класс экземпляр которого возвращает функция реализованная в dll. F>И dll собрана компилятором А. F>... F>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный? F>Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
Думаю, что даже если А==Б, то можно наступить на грабли, если, скажем, struct member alignment будет отличаться.
Re: Представление класса в памяти разными компиляторами
Здравствуйте, fanruten, Вы писали:
F>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный?
Никаких.
With best regards
Pavel Dvorkin
Re[2]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Никаких.
COM, тем не менее, обычно работает...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Представление класса в памяти разными компиляторами
E>Для таких гарантий должно совпадать соглашение о вызове методов. Дэйаут класса совпадать для этого не должен...
Для таких гарантий должно совпадать гораздо более важное, чем соглашения о вызове методов. Эти соглашения стандартизованы и могут вполне выполняться, поэтому если речь идет не о классах, то проблемы нет — stdcall можно вызывать откуда угодно, хоть из Delphi или VB.
А вот для классов — должен совпадать формат vtable и то, как указатель на нее в экземпляре хранится. Иначе первый же вызов виртуальной функции отправит вас в голубую даль. А вот это никем не стандартизируется.
With best regards
Pavel Dvorkin
Re[3]: Представление класса в памяти разными компиляторами
Здравствуйте, _stun_, Вы писали:
__>COM-объект и экземпляр C++-класса — прямо скажем, не одно и то же.
Конечно не одно и то же. Но обычно в С++ можно представить себе указатель на COM-объект, как указатель на абстрактный класс. И при inproc сервере стараются обойтись без маршалинга...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
E>>COM, тем не менее, обычно работает... PD>Э нет, там двоичный стандарт.
И компиляторы С++ под винду обычно предоставляют возможность описать интерфейс абстрактного класса так, чтобы он соответствовал стандарту COM... Кстати, ТС не упоминал именно виртуальные функции. Возможно его интересует вызов обычных
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Представление класса в памяти разными компиляторами
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, _stun_, Вы писали:
__>>COM-объект и экземпляр C++-класса — прямо скажем, не одно и то же.
E>Конечно не одно и то же. Но обычно в С++ можно представить себе указатель на COM-объект, как указатель на абстрактный класс. И при inproc сервере стараются обойтись без маршалинга...
Топикстартер нигде не говорил, что собирается ограничиваться исключительно абстрактными классами.
Re[6]: Представление класса в памяти разными компиляторами
Здравствуйте, _stun_, Вы писали:
__>Топикстартер нигде не говорил, что собирается ограничиваться исключительно абстрактными классами.
С обычными методами всё ещё проще. Если подавить inline'инг и не работать с полями напрямую, то достаточно, чтобы совпадало соглашение о вызове...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Представление класса в памяти разными компиляторами
Здравствуйте, Erop, Вы писали:
E>И компиляторы С++ под винду обычно предоставляют возможность описать интерфейс абстрактного класса так, чтобы он соответствовал стандарту COM... Кстати, ТС не упоминал именно виртуальные функции. Возможно его интересует вызов обычных
С обычными что-то сделать можно, если только не забывать, что для класса в том же VC используется thiscall, а в BCB ароде как fastcall, причем я даже не уверен, что fastcall_VC == fastcall_BCB
With best regards
Pavel Dvorkin
Re[6]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>С обычными что-то сделать можно, если только не забывать, что для класса в том же VC используется thiscall, а в BCB ароде как fastcall, причем я даже не уверен, что fastcall_VC == fastcall_BCB
На самом деле ещё будет проблема с декорацией имён методов. Так что химичить надо много и хитро. Но часто таки можно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Представление класса в памяти разными компиляторами
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, _stun_, Вы писали:
__>>Топикстартер нигде не говорил, что собирается ограничиваться исключительно абстрактными классами.
E>С обычными методами всё ещё проще. Если подавить inline'инг и не работать с полями напрямую, то достаточно, чтобы совпадало соглашение о вызове...
Ну, видите, сколько разных "если"... А топикстартер никаких ограничений на класс не накладывал.
Re[8]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Это мелочи, на это есть GetProcAddress
А звать как? Например операторы?
Я бы, лучше, при помощи DEF файла экспортировал из DLL методы сразу под несколькими именами...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Erop, Вы писали:
E>>Для таких гарантий должно совпадать соглашение о вызове методов. Дэйаут класса совпадать для этого не должен...
PD>Для таких гарантий должно совпадать гораздо более важное, чем соглашения о вызове методов. Эти соглашения стандартизованы и могут вполне выполняться, поэтому если речь идет не о классах, то проблемы нет — stdcall можно вызывать откуда угодно, хоть из Delphi или VB. PD>А вот для классов — должен совпадать формат vtable и то, как указатель на нее в экземпляре хранится. Иначе первый же вызов виртуальной функции отправит вас в голубую даль. А вот это никем не стандартизируется.
Здравствуйте, mike_rs, Вы писали:
_>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Здравствуйте, Erop, Вы писали:
E>>>Для таких гарантий должно совпадать соглашение о вызове методов. Дэйаут класса совпадать для этого не должен...
PD>>Для таких гарантий должно совпадать гораздо более важное, чем соглашения о вызове методов. Эти соглашения стандартизованы и могут вполне выполняться, поэтому если речь идет не о классах, то проблемы нет — stdcall можно вызывать откуда угодно, хоть из Delphi или VB. PD>>А вот для классов — должен совпадать формат vtable и то, как указатель на нее в экземпляре хранится. Иначе первый же вызов виртуальной функции отправит вас в голубую даль. А вот это никем не стандартизируется.
вот так правильно, сорри _>это то что назвается ABI
Re[9]: Представление класса в памяти разными компиляторами
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Это мелочи, на это есть GetProcAddress
E>А звать как? Например операторы?
Шут его знает. Как-нибудь сформировать указатель на метод-член класса, по нему и вызвать. В крайнем случае через asm.
E>Я бы, лучше, при помощи DEF файла экспортировал из DLL методы сразу под несколькими именами...
Что-то я плохо понимаю. А в EXE как вызывать будем ? С чего это компилятор для, скажем
A* pa = ...; pa->myfunc();
будет какое-то иное имя, кроме того, что он наманглил, использовать ?
With best regards
Pavel Dvorkin
Re[4]: Представление класса в памяти разными компиляторами
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>>Это мелочи, на это есть GetProcAddress E>>А звать как? Например операторы? PD>Шут его знает. Как-нибудь сформировать указатель на метод-член класса, по нему и вызвать. В крайнем случае через asm.
О! Это должно быть дико удобно
E>>Я бы, лучше, при помощи DEF файла экспортировал из DLL методы сразу под несколькими именами... PD>Что-то я плохо понимаю. А в EXE как вызывать будем ? С чего это компилятор для, скажем
PD>A* pa = ...; pa->>myfunc();
PD>будет какое-то иное имя, кроме того, что он наманглил, использовать ?
Он его и будет. В зависимости от того, какой компилятор компилил EXE будет звать разное имя. Соответственно мы можем экспортировать нужный метод под всеми нужными именами. А под некоторыми можно даже адаптеры экспортировать, на случай, если нет совсем никакого способа объявить класс так, чтобы быть совместимым с каким-то конкретным компилятором, можно ему подсунуть адаптеры...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Представление класса в памяти разными компиляторами
Здравствуйте, fanruten, Вы писали:
F>Допустим есть класс экземпляр которого возвращает функция реализованная в dll.
F>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный? F>Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
Функции-члены классов нельзя вызывать через границу DLL.
К членам-данным не-POD-классов нельзя обращаться через границу DLL.
Экземпляры классов нельзя удалять посредством delete через границу DLL.
Единственное, что можно делать — это передавать туда-сюда указатели на экземпляры. При условии, что вся работа с экземпляром остаётся по одну сторону границы DLL, а по другую стророну это просто обезличенный указатель на чёрный ящик.
COM-объекты можно использовать через границу DLL, при условии, что компиляторы по обе стороны поддерживают COM и все конструкции, употреблённые в описании интерфейса.
Re[2]: Представление класса в памяти разными компиляторами
Здравствуйте, Centaur, Вы писали:
C>Функции-члены классов нельзя вызывать через границу DLL. C>К членам-данным не-POD-классов нельзя обращаться через границу DLL.
При некоторой ловкости и для известных заранее пар компиляторов можно, тем не менее, но с приложением определённых усилий со стороны DLL, например.
C>Экземпляры классов нельзя удалять посредством delete через границу DLL.
Это да.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Представление класса в памяти разными компиляторами
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, fanruten, Вы писали:
F>>Допустим есть класс экземпляр которого возвращает функция реализованная в dll.
F>>Какие гарантии ,что при использование этой dll в коде собранном при помощи компилятора Б, вызов функций членов Foo будет правильный? F>>Ведь как я понимаю различные компиляторы могут по разному представлять экземпляр класса.
C>Функции-члены классов нельзя вызывать через границу DLL. C>К членам-данным не-POD-классов нельзя обращаться через границу DLL. C>Экземпляры классов нельзя удалять посредством delete через границу DLL.
C>Единственное, что можно делать — это передавать туда-сюда указатели на экземпляры. При условии, что вся работа с экземпляром остаётся по одну сторону границы DLL, а по другую стророну это просто обезличенный указатель на чёрный ящик.
C>COM-объекты можно использовать через границу DLL, при условии, что компиляторы по обе стороны поддерживают COM и все конструкции, употреблённые в описании интерфейса.
Еще. Классы, в которых есть non-POD типы, должны создаваться/удаляться там, где находится код функций, которые работают с этими классами. Иначе можно нарваться на различное вычисление размера структур и соотв. выделение неправильного объема памяти.
Re[3]: Представление класса в памяти разными компиляторами
Здравствуйте, Vain, Вы писали:
C>>>Экземпляры классов нельзя удалять посредством delete через границу DLL. E>>Это да. V>Это почему?
В том числе, имеется в виду, "через границу CRT DLL"
Нужно обеспечивать сочетаемость пар operator new-delete и конструктор-деструктор.
"Находятся в одном модуле" — более сильное условие, чем "сочетаемы друг с другом".
Но зато оно более просто проверяемое; не надо задумываться о зависимостях и совместимостях.
Скажем, если инлайновый деструктор обращается к каким-то инлайновым же статическим переменным (классика жанра: std::map) — нехорошо выйдет. А пойди проверь — куда ведут все эти инлайны...
Проще убедиться, что объект удаляется там, где создан, и всё.
Перекуём баги на фичи!
Re[5]: Представление класса в памяти разными компиляторами
Здравствуйте, Кодт, Вы писали:
C>>>>Экземпляры классов нельзя удалять посредством delete через границу DLL. E>>>Это да. V>>Это почему? К>В том числе, имеется в виду, "через границу CRT DLL" К>Нужно обеспечивать сочетаемость пар operator new-delete и конструктор-деструктор.
И не только это.
К>"Находятся в одном модуле" — более сильное условие, чем "сочетаемы друг с другом". К>Но зато оно более просто проверяемое; не надо задумываться о зависимостях и совместимостях. К>Скажем, если инлайновый деструктор обращается к каким-то инлайновым же статическим переменным (классика жанра: std::map) — нехорошо выйдет.
А какие там статические переменные?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: Представление класса в памяти разными компиляторами
Здравствуйте, Vain, Вы писали:
К>>Скажем, если инлайновый деструктор обращается к каким-то инлайновым же статическим переменным (классика жанра: std::map) — нехорошо выйдет. V>А какие там статические переменные?
За все на свете реализации не поручусь, а в динкумваровской (вижуаловской) — были.
Как известно, мап сделан на КЧД с фиктивными листьями. Так вот, эти листья можно воплотить 4 способами
— трактовать нулевые указатели как указатели на фиктивные листья — это приводит к ветвлениям и просадке производительности
— создать честные листья на куче — затраты памяти
— паттерн "нулевой объект"
— — экземпляр фиктивного листа прямо в теле мапа — затраты памяти (увеличение размера мапа); дорогая процедура обмена/присваивания мапов друг с другом (нужно корректировать всё дерево, чтобы гарантировать время жизни листьям)
— — экземпляр фиктивного листа в глобальной переменной
— — можно и пятый способ: разделяемое владение немногими неразличимыми между собой листьями... но это — опять просадка скорости и излишнее усложнение
Перекуём баги на фичи!
Re[7]: Представление класса в памяти разными компиляторами
Здравствуйте, Кодт, Вы писали:
К>>>Скажем, если инлайновый деструктор обращается к каким-то инлайновым же статическим переменным (классика жанра: std::map) — нехорошо выйдет. V>>А какие там статические переменные?
К>За все на свете реализации не поручусь, а в динкумваровской (вижуаловской) — были. К>Как известно, мап сделан на КЧД с фиктивными листьями. Так вот, эти листья можно воплотить 4 способами К>- трактовать нулевые указатели как указатели на фиктивные листья — это приводит к ветвлениям и просадке производительности К>- создать честные листья на куче — затраты памяти К>- паттерн "нулевой объект" К>- — экземпляр фиктивного листа прямо в теле мапа — затраты памяти (увеличение размера мапа); дорогая процедура обмена/присваивания мапов друг с другом (нужно корректировать всё дерево, чтобы гарантировать время жизни листьям) К>- — экземпляр фиктивного листа в глобальной переменной К>- — можно и пятый способ: разделяемое владение немногими неразличимыми между собой листьями... но это — опять просадка скорости и излишнее усложнение
А почему бы просто в корне не хранить? Или я что-то недопонял?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[8]: Представление класса в памяти разными компиляторами
Здравствуйте, Vain, Вы писали:
V>А почему бы просто в корне не хранить? Или я что-то недопонял?
В любом случае, это будет оверхед — по сравнению со статическим расшаренным объектом.
На самом деле, суть бага в том, что признак "это фиктивный лист" выводился не из того, что "это лист", а "адрес этого объекта равен адресу нашего синглетона". Эта проверка более дешёвая, но не выдерживает испытания многомодульностью (и нарушением ODR, на самом деле: ибо, у нас появляются несколько экземпляров статической переменной).
Перекуём баги на фичи!
Re[9]: Представление класса в памяти разными компиляторами
Здравствуйте, Кодт, Вы писали:
V>>А почему бы просто в корне не хранить? Или я что-то недопонял? К>В любом случае, это будет оверхед — по сравнению со статическим расшаренным объектом. К>На самом деле, суть бага в том, что признак "это фиктивный лист" выводился не из того, что "это лист", а "адрес этого объекта равен адресу нашего синглетона". Эта проверка более дешёвая, но не выдерживает испытания многомодульностью (и нарушением ODR, на самом деле: ибо, у нас появляются несколько экземпляров статической переменной).
Как я понимаю, дерево в реализации STL это технически тот же самый лист (т.к. STL требует линейной итерируемости от любого контейнера), т.е. ноды не перемещаемы в памяти, а т.к. минимум корень всегда будет существовать при хотябы одном листе, то это и есть нормальное решение. А неявные статические переменные в контейнерах и указатели на тело объекта в нодах это — от лукавого.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: Представление класса в памяти разными компиляторами
Здравствуйте, c-smile, Вы писали:
CS>Можно проверить поддерживает ли данный компилятор COM или нечто аналогичное. CS>Если "да" то в принципе можно делать предположения об однообразии. А лучше прямо COM интерфейсы и писать.
Насколько я могу припомнить, в COM нигде структурами данных не обмениваются. COM полагается только на способ размещения vptr и способ организации vtbl.
Re[10]: Представление класса в памяти разными компиляторами
Здравствуйте, Vain, Вы писали:
V>Как я понимаю, дерево в реализации STL это технически тот же самый лист (т.к. STL требует линейной итерируемости от любого контейнера), т.е. ноды не перемещаемы в памяти, а т.к. минимум корень всегда будет существовать при хотябы одном листе, то это и есть нормальное решение. А неявные статические переменные в контейнерах и указатели на тело объекта в нодах это — от лукавого.
Технически это всё-таки дерево. Полный обход дерева — за O(n), а одна итерация занимает переменное время.
Фиктивный корень, подобно фиктивным концам списка, у дерева тоже есть и является принадлежностью конкретного контейнера. Это end().
А фиктивные листья несут два признака: цвет:чёрный и лист:да.
Сейчас посмотрел на реализацию в VC8, там фиктивные листья совпадают с фиктивным корнем. Который тоже чёрный и пустой.
То есть, граф такого дерева выглядит вот так