Шаблон обработчика событий: неразрешенный внешний

0

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

Мероприятие:

#pragma once
class Event
{
protected:
   virtual ~Event() {};
};

Событие Хандер:

#pragma once
#include "Event.h"
#include "TypeInfo.h"
#include "HandlerFunctionBase.h"
#include <map>
#include <typeindex>

class EventHandler
{

public:
    void HandleEvent(const Event*);

    template < class T, class EventT >
    void RegisterEventFunc(T*, void (T::*memFn)(EventT*));

private:
    typedef std::map<std::type_index, HandlerFunctionBase* > Handlers;
    Handlers _handlers;
};

[...]

#include "EventHandler.h"

template < class T, class EventT >
void EventHandler::RegisterEventFunc(T* obj, void (T::*memFn)(EventT*))
{
    _handlers[std::type_index(typeid(EventT))]=
    new MemberFunctionHandler< T, EventT >(obj, memFn);
}

void EventHandler::HandleEvent(const Event* event)
{
    Handlers::iterator it = _handlers.find(std::type_index(typeid(*event)));
    if(it != _handlers.end())
    {
        it->second->exec(event);
    }
}

HandlerFunctionBase:

#pragma once
#include "Event.h"
class HandlerFunctionBase
{
public:
  virtual ~HandlerFunctionBase() {};
  void exec(const Event* event) {call(event);}

private:
  virtual void call(const Event*) = 0;
};

MemberFunctionHandler:

#pragma once
#include "handlerfunctionbase.h"
template < class T, class EventT >

class MemberFunctionHandler : public HandlerFunctionBase
{
public:
  typedef void (T::*MemberFunc)(EventT*);
  MemberFunctionHandler(T* instance, MemberFunc memFn) : _instance(instance), _function(memFn) {};

  void call(const Event* event)
  {
    (_instance->*_function)(static_cast< EventT* >(event));
  }

private:
  T* _instance;
  MemberFunc _function;
};

LogHandler

(Мой собственный класс, сначала попробуйте использовать систему)

#pragma once
#include "EventHandler.h"
#include "LogEvent.h"
class LogHandler
{
public:
    LogHandler(EventHandler*);
    ~LogHandler(void);
private:
    void Handle(LogEvent*);
};
#[...]
#include "LogHandler.h"


LogHandler::LogHandler(EventHandler *handler)
{
    //This line causes the error
    handler->RegisterEventFunc<LogHandler, LogEvent>(this, &LogHandler::Handle);
}


LogHandler::~LogHandler(void)
{
}
void LogHandler::Handle(LogEvent* e)
{
}

Что я получаю при попытке скомпилировать это:

Ошибка 1 ошибка LNK2019: неразрешенный внешний символ "public: void __thiscall EventHandler :: RegisterEventFunc (класс LogHandler *, void (__thiscall LogHandler :: *) (класс LogEvent *))" (?? $ RegisterEventFunc @VLogHandler @@VLogEvent @@@EventHandler @@QAEXPAVLogHandler @@P81 @AEXPAVLogEvent @@@Z @Z), на который ссылается функция "public: __thiscall LogHandler :: LogHandler (класс EventHandler *)" (? 0LogHandler @@QAE @PAVEventHandler @@@Z) D:\Dropbox\c++\D-Tris\D-Tris\D-Tris\LogHandler.obj D-Tris

Как RegEventFunc не разрешен? Это четко реализовано!?

  • 2
    Вам нужно поместить реализацию шаблонного метода в заголовочный файл.
  • 0
    Находятся ли определения функций вашего шаблона в файле .cpp? Если это так, вам нужно сделать их доступными для кода, который создает экземпляры шаблонов. На практике самый простой способ сделать это - поместить их в заголовочный файл.
Показать ещё 2 комментария
Теги:
handler
events

1 ответ

1
Лучший ответ

Компилятор должен знать типы, используемые для создания экземпляра шаблона, чтобы фактически генерировать код. Но он также генерирует код для каждой единицы перевода (один.cpp файл) независимо. Он не "игнорирует файлы".

Таким образом, в момент, когда вы имеете определение EventHandler::RegisterEventFunc, он не может знать, с какими параметрами он будет создан, если он используется (создается экземпляр) вне этой единицы перевода.

В LogHandler он знает о шаблоне EventHandler::RegisterEventFunc (из файла заголовка), но поскольку определения нет, он просто предполагает создание экземпляра RegisterEventFunc<LogHandler, LogEvent> другом месте и не испускает никаких ошибок.

Когда он связывается вместе, компоновщик обнаруживает, что никто фактически не RegisterEventFunc<LogHandler, LogEvent> экземпляр RegisterEventFunc<LogHandler, LogEvent> поэтому он не может связывать его вместе и испускает ошибку, которую вы видите.

Что вы можете сделать с этим:

1) переместите определение EventHandler::RegisterEventFunc в EventHandler.h. (ИМХО, это обычное решение)

2) Или принудительное явное создание экземпляра в EventHandler.cpp, например

template
void EventHandler::RegisterEventFunc<LogHandler, LogEvent>
       (LogHandler* obj, void (LogHandler::*memFn)(LogEvent*))

Это "решение" разрушает инкапсуляцию, добавляет множество зависимостей и будет адским.

3) Или используйте exported шаблоны. C++ поддерживает (поддерживается) шаблоны так, как вы хотите использовать их через export ключевых слов. Он поддерживался только Комо и ICC (ни один из GCC, CLANG, MSVC никогда не поддерживал это), и теперь он удаляется из стандарта (в N3690, [diff.cpp03.temp] (приложение C.2.7, на стр. 1240) Стандарты говорят: A valid C++ 2003 declaration containing export is ill-formed in this International Standard.). Даже не пытайтесь, я добавил это только ради полноты.

Некоторые связанные вопросы, которые могут вам интересны:

Как явным образом создаю экземпляр функции шаблона?

Использование ключевого слова export с шаблонами

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

EDIT: к вашему другому вопросу: вы не можете удалить конструктор const из переменной static_cast. Константа кастинга потенциально опасна и ее следует избегать, но если вам это абсолютно необходимо, вы можете сделать это с помощью const_cast< EventT* >(event).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню