Я сейчас перехожу из С# в 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);
}
}
#pragma once
#include "Event.h"
class HandlerFunctionBase
{
public:
virtual ~HandlerFunctionBase() {};
void exec(const Event* event) {call(event);}
private:
virtual void call(const Event*) = 0;
};
#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;
};
(Мой собственный класс, сначала попробуйте использовать систему)
#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 не разрешен? Это четко реализовано!?
Компилятор должен знать типы, используемые для создания экземпляра шаблона, чтобы фактически генерировать код. Но он также генерирует код для каждой единицы перевода (один.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)
.