Смущен неразрешенной ошибкой внешнего символа

Я пытаюсь создать библиотеку-оболочку с помощью компилятора VC++.

ErlDriver.c

#define __WIN32__
#define DLL_EXPORT __declspec(dllexport)

#include "erl_driver.h"

DLL_EXPORT int _driver_output(ErlDrvPort port, char *buf, int len) {
    return driver_output(port, buf, len);
}

build.bat

cl /I%ERL_DRIVER_H% /LD /MD ErlDriver.c

Когда я пытаюсь создать это, я получаю следующую ошибку компоновщика:

ErlDriver.obj: ошибка LNK2019: неразрешенный внешний символ _WinDynDriverCallbacks, указанный в функции __driver_output

erl_win_dyn_driver.h (включен в erl_driver.h)

typedef struct {
    WDD_FTYPE(driver_output) *driver_output;
    // a ton more of those
} TWinDynDriverCallbacks;

extern TWinDynDriverCallbacks WinDynDriverCallbacks;

#define driver_output (WinDynDriverCallbacks.driver_output)

Итак, как видите, WinDynDriverCallbacks объявлен defined.

Что же тогда может быть причиной ошибки компоновщика?


person David Brown    schedule 27.08.2009    source источник


Ответы (4)


Существует тонкая разница между «объявлением» чего-либо и «определением» этого в C или C++. Когда вы объявляете его, он сообщает компилятору, что определенный символ будет определен где-то еще — это может позволить коду использовать этот символ без необходимости просмотра фактического определения. Вам все еще нужно определить символ где-то в коде, который связан, иначе вы получите сообщение об ошибке, которое видите.

Например, это объявление символа WinDynDriverCallbacks:

extern TWinDynDriverCallbacks WinDynDriverCallbacks;

В вашем коде есть это объявление - оно позволяет коду, использующему символ, успешно компилироваться (но не связываться).

Вам нужно добавить определение где-то:

TWinDynDriverCallbacks WinDynDriverCallbacks;

Определение должно находиться где-то в файле исходного кода (обычно не в заголовочном файле). Это говорит компилятору выделить место в объектном коде для этого объекта и позволяет программе успешно компоноваться.

person 1800 INFORMATION    schedule 27.08.2009
comment
Я совершенно не понимаю, почему это работает, но это точно работает. Почему каждый драйвер порта Erlang, который я могу найти, не должен использовать это? - person David Brown; 28.08.2009
comment
Они должны делать что-то эквивалентное - или они не используют этот символ, поэтому компоновщику не нужно иметь определение - person 1800 INFORMATION; 28.08.2009
comment
Например, может быть файл .lib, который вам нужно связать с определением, — он может быть частью используемого вами SDK. - person 1800 INFORMATION; 28.08.2009
comment
Думаю, я не буду слишком много думать об этом. По крайней мере, это работает сейчас. Спасибо! - person David Brown; 28.08.2009

Нет, не определено (по крайней мере, в том, что вы процитировали). Это заявлено. Ключевое слово «extern» означает, что «определение этого символа появляется в другой единице компиляции (исходном файле)». Вы должны быть связаны с объектным файлом (или библиотекой), полученным в результате компиляции исходного файла, который определяет этот символ.

person Tyler McHenry    schedule 27.08.2009
comment
Нет объектного файла или библиотеки. Все, что у меня есть, это заголовочные файлы. Инструкции, которые я использую, не связаны ни с какими библиотеками. - person David Brown; 28.08.2009
comment
Заголовочные файлы не содержат переменных или кода. Они содержат только декларации. Вы абсолютно ничего не можете сделать только с файлами заголовков. Либо ваши инструкции неверны, либо вы их неправильно понимаете. - person Tyler McHenry; 28.08.2009
comment
Я только что заметил, что слово Driver продолжает появляться. Вы пытаетесь создать драйвер устройства Windows? Если это так, то символ может быть в ядре Windows, и кто-то, кто больше разбирается в написании драйверов для Windows, может прокомментировать, как добиться того, чего вы хотите. - person Tyler McHenry; 28.08.2009
comment
Так как же структура TWinDynDriverCallbacks может появиться в другом исходном файле, если она объявлена ​​в том же файле, в котором используется? - person David Brown; 28.08.2009
comment
Кроме того, это не драйвер Windows. Это драйвер порта Erlang (в основном DLL, которая вызывается из программы Erlang). - person David Brown; 28.08.2009
comment
Объявления означают, что это имя чего-то, что появится в этой программе. Определения означают, что необходимо выделить память (для переменной) или сгенерировать код (для функции) в этой точке программы и присвоить ей это имя. Любой заданный символ может иметь столько объявлений, сколько вы хотите, если все они согласны с тем, какие типы используются. Символ может иметь только одно определение, потому что его память (или код) может существовать только в одном месте исполняемого файла. Объявления используются, чтобы сообщить компилятору об используемых вами символах, которые поступают из файлов, отличных от того, который вы компилируете. - person Tyler McHenry; 28.08.2009
comment
Спасибо за объяснение. Я недостаточно заглянул в код. WinDynDriverCallbacks определяется при вызове DRIVER_INIT, чего я не делаю, поскольку это не настоящий драйвер порта. Так что я просто определил это сам, и все работает. - person David Brown; 28.08.2009

У меня возникла очень похожая проблема при создании NIF в Windows. Неразрешенный внешний символ _WinDynNifCallbacks. Оказывается, это определяется макросом ERL_NIF_INIT, и в моем случае весь макрос нужно было заключить во внешний блок C.

то есть это не удалось

extern "C" ERL_NIF_INIT(...)

пока это удалось

extern "C"
{
   ERL_NIF_INIT(...)
}

Я сильно подозреваю, что эта проблема связана с той же проблемой, но с макросом DRIVER_INIT для драйвера порта erlang.

person Gary    schedule 22.12.2011

Driver_Init — это основной цикл, который объявляет «TWinDynDriverCallbacks WinDynDriverCallbacks;» но он правильно объявлен в многострочном определении для driver_init. Вам не нужно оборачивать его в extern "c".

поскольку эта тема поднималась около миллиона раз при попытке настроить мой драйвер порта erlang для barebone-систем, я скажу это здесь. Я работаю над книгой Джо Армстронга по программированию erlang, глава 12, методы взаимодействия. Использование erl5.9 и vs2010.

Код в книге содержал пропуск и ошибку в example1_lib.c. Хотя ошибка, скорее всего, связана с возрастом книги и изменениями версии erlang.

Необходимо установить (#define WIN32) в самом верху файла example1_lib.c, иначе erlang по умолчанию использует все параметры Linux.

Во-вторых, необходимо изменить (int bufflen) на (ErlDrvSizeT bufflen) в example_drv_output.

После этого построили чисто.

person jason    schedule 14.02.2013