Как решить неразрешенную внешнюю проблему при использовании пакетов C ++ Builder? - PullRequest
8 голосов
/ 28 апреля 2010

Я экспериментирую с перенастройкой своего приложения, чтобы использовать пакеты очень часто. И я, и другой разработчик, проводящий аналогичный эксперимент, сталкиваются с некоторыми проблемами при компоновке с использованием нескольких различных пакетов. Мы, наверное, оба делаем что-то не так, но, черт возьми, что знает :)

Ситуация такова:

  • Первый пакет, PackageA.bpl, содержит класс C ++ FooA. Класс объявляется с директивой PACKAGE.
  • Второй пакет, PackageB.bpl, содержит класс, унаследованный от FooA, называемый FooB. Он включает FooB.h, пакет создается с использованием пакетов времени выполнения и ссылки на PackageA путем добавления ссылки на PackageA.bpi.

  • При сборке PackageB он компилируется нормально, но соединение не удается с несколькими неразрешенными внешними объектами, первые из которых:

    • [ILINK32 Error] Error: Unresolved external '__tpdsc__ FooA' referenced from C:\blah\FooB.OBJ
    • [ILINK32 Error] Error: Unresolved external 'FooA::' referenced from C:\blah\FooB.OBJ
    • [ILINK32 Error] Error: Unresolved external '__fastcall FooA::~FooA()' referenced from blah\FooB.OBJ

    и т.д.

Запуск TDump на PackageA.bpl показывает:

Exports from PackageA.bpl
  14 exported name(s), 14 export addresse(s).  Ordinal base is 1.
  Sorted by Name:
    RVA      Ord. Hint Name
    -------- ---- ---- ----
    00002A0C    8 0000 __tpdsc__ FooA
    00002AD8   10 0001 __linkproc__ FooA::Finalize
    00002AC8    9 0002 __linkproc__ FooA::Initialize
    00002E4C   12 0003 __linkproc__ PackageA::Finalize
    00002E3C   11 0004 __linkproc__ PackageA::Initialize
    00006510   14 0007 FooA::
    00002860    5 0008 FooA::FooA(FooA&)
    000027E4    4 0009 FooA::FooA()
    00002770    3 000A __fastcall FooA::~FooA()
    000028DC    6 000B __fastcall FooA::Method1() const
    000028F4    7 000C __fastcall FooA::Method2() const
    00001375    2 000D Finalize
    00001368    1 000E Initialize
    0000610C   13 000F ___CPPdebugHook

Таким образом, класс определенно экспортируется и доступен для связи. Я вижу записи о конкретных вещах, которые ILink32 говорит, что ищет и не находит. Запуск TDump в файле BPI показывает похожие записи.

Другая информация

Класс происходит от TObject, хотя изначально до рефакторинга в пакеты это был обычный класс C ++. (Более подробно ниже. Кажется, «безопаснее» использовать классы в стиле VCL при попытке решить проблемы с очень Delphi-ишной вещью, подобной этой. В любом случае, изменение этого параметра только меняет порядок неразрешенных внешних объектов, чтобы сначала не находить Method1 и Method2, затем другие.)

Декларация для FooA:

class PACKAGE FooA: public TObject {
public:
   FooA();
   virtual __fastcall ~FooA();
   FooA(const FooA&);
   virtual __fastcall long Method1() const;
   virtual __fastcall long Method2() const;
};

и FooB:

class FooB: public FooA {
public:
   FooB();
   virtual __fastcall ~FooB();
   ... other methods...
};

Все методы определенно реализованы в файлах .cpp, поэтому их не найти, потому что они не существуют! Файлы .cpp также содержат #pragma package(smart_init) в верхней части, под заголовками include.

Вопросы, которые могут помочь ...

  • Надежны ли пакеты с использованием C ++ или они могут использоваться только с кодом Delphi?
  • Является ли ссылка на первый пакет путем добавления ссылки на его BPI правильной - это то, как вы должны это делать? Я мог бы использовать LIB, но, похоже, второй пакет стал намного больше, и я подозреваю, что он статически связан с содержимым первого.
  • Можем ли мы использовать директиву PACKAGE только для классов TObject? Нет предупреждения компилятора, использующего его на стандартных классах C ++.
  • Является ли разбиение кода на пакеты лучшим способом достижения цели изоляции кода и связи через определенные уровни / интерфейсы? Я изучал этот путь, потому что он, похоже, C ++ Builder / Delphi Way, и если он работал, он выглядит привлекательно. Но есть ли лучшие альтернативы?
  • Я очень новичок в использовании пакетов и раньше знал о них только благодаря использованию компонентов. Любые общие слова совета были бы великолепны!

Мы используем C ++ Builder 2010. Я сфабриковал имена классов и методов в приведенных выше примерах кода, но кроме этого детали именно то, что мы видим.

Ответы [ 3 ]

9 голосов
/ 28 апреля 2010

Нерешенный внешний

Неразрешенный внешний элемент в вашем случае, по-видимому, связан с тем, что компилятор не может найти путь к данным пакета. Вы должны узнать, если:

  • Путь существует в списке путей поиска компилятора.
  • Пакет существует в каталоге пакета по умолчанию.

Если один из них верен, то путь не является проблемой. Однако, как Riho также упоминает, что это наиболее вероятная причина проблемы. * Вики-документация Embarcadero гласит следующее о неразрешенной внешней ошибке :

На указанный символ ссылаются в данном модуле, но он нигде не определен в наборе объектных файлов и библиотек, включенных в ссылку. Убедитесь, что символ написан правильно.

Обычно вы увидите эту ошибку от компоновщика символов C или C ++, если произойдет любое из следующих действий:

  • Вы неправильно сопоставили объявления символов типов __pascal и __cdecl в разных исходных файлах.
  • Вы пропустили имя объектного файла, необходимого вашей программе. Вам необходимо вручную добавить все необходимые пакеты в список Требуется.
  • Вы не указали ссылку в библиотеке эмуляции.

Если вы связываете код C ++ с модулями C, вы, возможно, забыли обернуть внешние объявления C в extern «C».

Возможно также несоответствие регистра между двумя символами.

Источник: Неразрешенный внешний «символ», на который ссылается «модуль» .

Поскольку, как видно из - хотя и изменены имена классов - это не орфографическая ошибка. Вы также заявляете, что добавили пакет в список требований, поэтому мы также исключаем это. Поскольку вы не ссылаетесь на модули C, мы можем также пропустить эту часть. Так что это указывает на проблемы с каталогом.

О других вопросах

Все ваши вопросы действительно интересны, и многие из них - это вопросы, ответы на которые я сам искал, когда начал разрабатывать пакеты и компоненты для C ++ Builder.

Надежны ли пакеты с использованием C ++?

Пакеты - это отличное решение для использования в C ++ Builder. Оба C ++ Builder созданы для поддержки пакетов и написанной на языке Pascal инфраструктуры VCL. Это означает, что некоторые реализации в C ++ Builder отличаются от других компиляторов. Это необходимо для обеспечения совместимости языка с его родным братом Delphi. По этой причине вы можете использовать пакеты в C ++ Builder почти так же легко, как при использовании Delphi.

Корректна ли ссылка на первый пакет путем добавления ссылки на его BPI?

Чтобы начать со второй части вашего вопроса, использование файла lib увеличивает размер вашего пакета просто потому, что он использует статическое связывание, поэтому ваше предположение верно. Теперь вернёмся к первой части вопроса, ссылка на пакет в порядке, добавив ссылку на его BPI. Но вам нужно убедиться, что переменная пути установлена ​​правильно, как подсказывает Riho в своем ответе.

Лично я всегда проверяю, чтобы мои пакеты были в правильных каталогах в вашей папке пользователя, расположение которых зависит от вашей версии Delphi и версии операционной системы. Насколько я помню, он находится в разделе «Документы и настройки» \ все пользователи \ общие документы \ Rad studio (номер версии) \ пакетов, но я могу ошибаться.

Можем ли мы использовать директиву PACKAGE только для классов TObject?

Макрос PACKAGE разрешен в __declspec(package), его можно сравнить с __declspec(dllexport). Разница между ними заключается в том, что package используется при объявлении в пакете, а dllexport используется при объявлении в DLL. На официальных форумах embarcadero есть тема под названием __declspec (пакет) против __declspec (dllexport) . Автор оригинального сообщения также задает ваш точный вопрос по этому поводу, но, к сожалению, эта часть вопроса остается без ответа.

Однако у меня есть теория, и я должен подчеркнуть, что это не более чем теория. Реми Лебо пишет в ответ на вопрос в сообщении на форуме:

__declspec (dllexport) можно использовать для простых функций, переменных данных и не-VCL классы, и могут быть использованы в простых DLL. __declspec (пакет) является используется для компонентов VCL и может использоваться только с пакетами.

Так что после прочтения его ответа мне кажется, что пакет просто экспортирует класс, как это делает dllexport. И поскольку dllexport, насколько я могу судить по его ответу, должен использоваться в простых DLL, только вы должны использовать пакет для экспорта (даже) не VCL классов из пакета.

Что интересно во всем этом, насколько я помню, пакет по сути является DLL, но я должен признать, что не могу найти или запомнить источник этой информации, так что возьмите с собой немного соли.

Является ли разбиение кода на пакеты лучшим способом достижения цели выделения кода?

Пакеты имеют несколько сильных сторон при создании повторно используемых компонентов для VCL. Очевидно, что использование пакетов ограничивает пользователя в использовании C ++ Builder или Delphi, но для компонентов, написанных с использованием инфраструктуры VCL, это отличный выбор. Правильно написанные пакеты могут облегчить повторное использование компонентов, и я считаю, что это предпочтительный метод распределения компонентов для VCL.

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

Существует ли какой-либо лучший подход к изоляции вашего кода, на самом деле зависит от проекта, над которым вы работаете. Мне нравится хранить код, который связывается посредством использования классов VCL в пакетах, но код, который не требует использования каких-либо классов VCL в обычных библиотеках. Имейте в виду, что вы можете легко использовать классы VCL в DLL, но вам нужно обрабатывать особые случаи, если вы решите экспортировать функции с классами VCL String в качестве параметров или возвращаемых значений.

Какие-нибудь общие слова совета?

Я сам не самый опытный разработчик пакетов, но я обнаружил, что отключение ссылок во время выполнения часто решает многие мои проблемы, хотя исправить некоторые проблемы для вашего собственного кода довольно просто, вы часто можете столкнуться с этим. Сторонние компоненты, которые имеют проблемы с этим. Сказав это, я не фанат распространения своих пакетов вместе с моим заявлением, как требуется в этом случае. Но, честно говоря, это дело вкуса.

Лично мне было трудно найти правильные ответы на многие мои вопросы, когда я начал создавать компоненты и пакеты. Официальный файл справки не является наиболее информативным по этому вопросу, но, просматривая исходный код VCL, он часто дает вам лучший ответ на ваш вопрос. Кроме того, есть несколько других сайтов, которые могут оказать помощь, хотя многие сайты ориентированы на Delphi, но к этому нужно привыкнуть.

В Delphi Wikia есть несколько хороших статей о создании компонентов, в частности Создание компонентов и Создание пакетов Существует также BCB Journal , который является одним из немногих C ++ Строитель конкретных сайтов, у него есть несколько прекрасных статей и приемлемый форум. Страницы Delphi на About.com также являются хорошим источником информации, я нашел там много полезных советов и полезных сведений, в частности: Создание пользовательских компонентов Delphi - внутри и снаружи .

2 голосов
/ 28 апреля 2010

Возможно глупый вопрос, но компоновщик находит ваши BPI / BPL-файлы по правильному пути? Однажды я создал приложение в BCB5, которое использовало несколько связанных пакетов, но не помню, было ли в них что-то особенное.

1 голос
/ 30 января 2013

Для меня # Прагма пакет (smart_init, слабый) в файле cpp решил проблему. Смотри также http://flylib.com/books/en/3.264.1.27/1/ Файл cpp-> obj статически связывается, не влияя ни на что другое.

...