2013-11-22 3 views
0

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

Событие:

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

Событие Hander:

#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

Как не разрешен RegisterEventFunc? Это ясно реализовано!?

+2

Вам нужно поместить реализацию шаблонного метода в файл заголовка. – v154c1

+0

Являются ли ваши определения функций шаблонов в .cpp-файле? Если это так, вы должны сделать их доступными для кода, который создает шаблоны. На практике самый простой способ сделать это - поместить их в файл заголовка. – juanchopanza

+0

Я делаю? ЗАЧЕМ? Я думал, что C++ игнорирует файлы и просто сшивает все вместе!? – pixartist

ответ

1

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

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

В LogHandler, он знает о шаблоне EventHandler::RegisterEventFunc (из заголовка файла), но нет никакого определения, оно просто предполагает Theres инстанциации 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) Standars говорит: A valid C++ 2003 declaration containing export is ill-formed in this International Standard.). Даже не пытайтесь, я добавил это только ради полноты.

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

How do I explicitly instantiate a template function?

Using export keyword with templates

Why can templates only be implemented in the header file? (это на самом деле кажется дубликата ....)

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

Смежные вопросы