事件处理程序模板:未解析的外部

时间:2013-11-22 13:56:16

标签: c++ events handler

我正在从C#过渡到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(__ thishisall LogHandler :: *)(类LogEvent *))”(?? $ RegisterEventFunc @ VLogHandler @@ VLogEvent @@@ EventHandler @@ QAEXPAVLogHandler @@ P81 @ AEXPAVLogEvent @@@ Z @ Z)在函数“public:__thiscall LogHandler :: LogHandler(class EventHandler *)”中引用(?? 0LogHandler @@ QAE @ PAVEventHandler @@@ Z) D:\ Dropbox \ C ++ \ D-Tris \ D-Tris \ D-Tris \ LogHandler.obj D-Tris

RegisterEventFunc是如何解决的?它已经明确实施了!?

1 个答案:

答案 0 :(得分:1)

编译器必须知道用于实例化模板以实际生成代码的类型。但它也为每个翻译单元(一个.cpp文件)独立生成代码。它不会“忽略文件”。

所以当你有EventHandler::RegisterEventFunc的定义时,如果它在这个翻译单元之外被使用(实例化),它就无法知道它将被实例化的参数。

LogHandler中,它知道模板EventHandler::RegisterEventFunc(来自头文件),但由于没有定义,它只是假定在其他地方实例化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使用它们。它仅由Comeau和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.)。甚至不尝试,我为了完整起见而添加它。

您可能感兴趣的一些相关问题:

How do I explicitly instantiate a template function?

Using export keyword with templates

Why can templates only be implemented in the header file?(这实际上看似重复......)

编辑:对于您的其他问题:您无法通过const从变量中移除static_cast限定符。抛弃常量是有潜在危险的,应该避免,但如果你绝对需要它,你可以通过const_cast< EventT* >(event)来完成。