Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Классы в библиотеках


Автор: EvgeniyK77 15.8.2012, 08:01
Подскажите, пожалуйста, как корректно разместить класс в статической библиотеке (.a)?

Поясню суть. Есть некоторый класс, который я скомпилировал в Linux с помощью mingw. Полученную библиотеку и заголовочный файл дал другому человеку, который её использовал в своей программе. Всё было здорово. Всё чудно линковалось и работало... До поры до времени. После очередного обновления mingw (у него) программа стала падать в одном из моих методов. При этом линковщик не ругался. После пересборки моей библиотеки на его машине падения прекратились. Стал разбираться. Оказалось, что mingw вдруг решил для методов поменять порядок аргументов (this поместил в другое место)! Я, конечно, понимаю, что классы в библиотеках - это не очень хорошо, т.к. разные компиляторы по-разному генерят имена для методов, и библиотека, собранная одним компилятором, может не подлинковаться другим. Но компилятор ведь один! Да и линковка прошла успешно...

Быть может, есть какие-то стандартные способы, типа extern "C"?

Если интересно, вот тест (библ. собрана на Fedora 14, а программа - на Fedora 17):
Код

// TestClassLib.hpp
#ifndef TEST_CLASS_LIB_HPP
#define TEST_CLASS_LIB_HPP

class TestClass1 {
public:
    void Fun1(int a0, int a1, int a2, int a3);
};

#endif

Код

// TestClassLib.cpp
#include <stdio.h>
#include "TestClassLib.hpp"

void TestClass1::Fun1(int a0, int a1, int a2, int a3) {
    printf("%d %d %d %d (this=%d)\n", a0, a1, a2, a3, (int)this); fflush(stdout);
}

Код

// Тестовая программа
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "TestClassLib.hpp"

int main() {
    TestClass1 zzz;
    zzz.Fun1(10,11,12,13);
    return 0;
}

Программы выводит:
Код

11 12 13 2009226388 (this=10)

Автор: Cheloveck 15.8.2012, 10:11
Цитата(EvgeniyK77 @  15.8.2012,  09:01 Найти цитируемый пост)
this поместил в другое место

this передаётся в регистре ECX, это конвенция http://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%B3%D0%BB%D0%B0%D1%88%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B2%D1%8B%D0%B7%D0%BE%D0%B2%D0%B0#thiscalll и никаких вольностей компилятор тут позволять себе не должен. Порядок аргументов тоже определён thiscall. Проблема где-то в другом месте.

Автор: korian 15.8.2012, 11:21
Цитата(Cheloveck @  15.8.2012,  09:11 Найти цитируемый пост)
this передаётся в регистре ECX, это конвенция thiscall 

Это всего лишь соглашение, а не стандарт. И никто его поддерживать не обязан.
Да и что делать на системах, в которых регистра ECX изначально не было?
http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/281008-gcc-thiscall-calling-convention-linux-win32-mingw.html

EvgeniyK77
Я точно не знаю, но поидее надо или перекомпиливать все одним компилятором или использовать только cdecl С-функции, т.е. никакого C++ наверху торчать не должно.

Автор: EvgeniyK77 17.8.2012, 09:10
Спасибо!

Стал разбираться с модификаторами и узнал, что в mingw 4.7 умолчальный модификатор методов изменился на __thiscall. В 4.5 такого вообще нет.
Если у метода явно поставить __fastcall, __stdcall или __cdecl, то всё работает!

Какой из этих модификаторов предпочтительнее?

Автор: leniviy 17.8.2012, 09:17
Цитата(EvgeniyK77 @  15.8.2012,  08:01 Найти цитируемый пост)
Если интересно, вот тест

не мог бы ты приаттачить 2 версии тестовой либы? Одну собранную на старом MinGW, другую - на новом.
Естественно, с -g -O0.

Цитата(EvgeniyK77 @  15.8.2012,  08:01 Найти цитируемый пост)
При этом линковщик не ругался.


По идее, у методов __cdecl и __thiscall должна быть разная сигнатура. Visual C++:
Код

"public: void __cdecl    TestClass::nnn(void)" (?nnn@TestClass@@QAAXXZ)
"public: void __thiscall TestClass::nnn(void)" (?nnn@TestClass@@QAEXXZ)

@@QAAXXZ vs @@QAEXXZ

То есть, программа не должна была компилироваться.

Автор: EvgeniyK77 17.8.2012, 10:29
Сделано!

Автор: leniviy 17.8.2012, 10:45
спасибо. Похоже, что в mingw gcc сигнатура одинаковая.

Цитата(EvgeniyK77 @  17.8.2012,  09:10 Найти цитируемый пост)
Какой из этих модификаторов предпочтительнее?

Идеального нет. Я бы использовал __thiscall, так как он быстрее:

Код

#if defined(_WIN32) && defined(__MINGW32__)
  #define MYCALL __thiscall
#else
  #define MYCALL
#endif


А на старом компиляторе пускай не собирается. Кто умеет ставить MinGW, сможет поставить и новый MinGW

Автор: Randajad 17.8.2012, 13:04
Кто сказал, что классы в библиотеках - плохая идея? Не говорите таких глупостей. smile
GCC версии < 4.7 передает this через стэк. GCC версии >= 4.7 передает this через ecx на x86 платформах. Это сделано для обеспечения совместимости с MSVC либами. Другого не дано. smile На этом обратная совместимость заканчивается. Кстати, LTO библиотеки не совместимы даже с разными билдами компилятора в пределах одной версии.

Не нужно указывать никаких extern/__thiscall в статических либах. Зачем? Просто используйте оба компилятор или выше 4.6, или равный и ниже ему.

Добавлено через 4 минуты и 9 секунд
To leniviy:
Вопрос насчет быстроты __thiscall спорный. Может, тогда fastcall еще быстрее? Однако, его никто не использует. 

Автор: leniviy 17.8.2012, 13:46
Цитата(Randajad @  17.8.2012,  13:04 Найти цитируемый пост)
Не нужно указывать никаких extern/__thiscall в статических либах. Зачем?

Это значит, оставить всё как есть.
Лучше пускай программа валится на сборке, чем при работе. А для этого нужно, чтобы несовместимую библиотеку нельзя было подключить.

До совместимости с MSVC далеко: имена C++ по-разному декорируются.

Цитата(Randajad @  17.8.2012,  13:04 Найти цитируемый пост)
опрос насчет быстроты __thiscall спорный. Может, тогда fastcall еще быстрее? Однако, его никто не использует.

Здесь выбор только между двумя вариантами: cdecl и thiscall.

Автор: Randajad 17.8.2012, 14:42
Это все хорошо, но этот шаг сделан именно ради улучшения совместимости с MSVC. Такие "валы" видны сразу же при первом запуске. Виноват человек и пихать везде MYCALL - не решение проблемы.
Почему между двумя? Fastcall поддерживает классы тоже.

Автор: EvgeniyK77 20.8.2012, 07:29
Цитата

Такие "валы" видны сразу же при первом запуске.
Видны... Это да. Но причина очень даже не очевидна. Программа большая, и мы, пока не пересобрали либу, думали, что поломалось где-то в другом месте, а проявилось здесь.
На различия компиляторов я не думал, т.к. считал, что либа, в случае чего, просто не подлинкуется.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)