Reputation: 8206
template <typename SenderType__, typename... Args__>
class CORE_API Event
{
public:
typedef typename std::function<void(const SenderType__*, Args__ ...)> EventHandler;
Event& operator+=(const EventHandler& toSubscribe)
{
Subscribe(toSubscribe);
return *this;
}
void operator()(const SenderType__* sender, Args__ ... args) const
{
Invoke(sender, args...);
}
void Subscribe(const EventHandler& toSubscribe)
{
std::lock_guard<std::mutex> locker(m_callbackMutex);
m_callbacks.push_back(toSubscribe);
}
void Clear()
{
std::lock_guard<std::mutex> locker(m_callbackMutex);
m_callbacks.clear();
}
void Invoke(const SenderType__* sender, Args__ ... args) const
{
std::lock_guard<std::mutex> locker(m_callbackMutex);
for (auto iter = m_callbacks.begin(); iter != m_callbacks.end(); ++iter)
{
(*iter)(sender, args...);
}
}
private:
std::vector<EventHandler> m_callbacks;
mutable std::mutex m_callbackMutex;
};
template class CORE_API Event<std::string, std::string>;
In a consumer of the DLL....
TEST(EventTest, TestEventFiresAndPassesArgs)
{
Event<std::string, std::string> event;
event += &TestFunction;
event += &TestFunction2;
std::string sender = "TestEventFiresAndPassesArgs";
std::string arg = "boo!";
event.Invoke(&sender, arg);
ASSERT_EQ(sender, testEventFiresAndPassesArgsSenderName);
ASSERT_EQ(arg, testEventFiresAndPassesArgsTestArg);
ASSERT_EQ(sender + "Function2", testEventFiresAndPassesArgsSenderName2);
ASSERT_EQ(arg + "Function2", testEventFiresAndPassesArgsTestArg2);
}
Then the linker output is:
EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > & __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::operator+=(class std::function<void __cdecl(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)> const &)" (__imp_??Y?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAAAEAV012@AEBV?$function@$$A6AXPEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z@std@@@Z) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::Invoke(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)const " (__imp_?Invoke@?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEBAXPEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V45@@Z) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??0?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAA@XZ) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>EventTest.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl CompanyName::Utils::Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >::~Event<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >(void)" (__imp_??1?$Event@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Utils@CompanyName@@QEAA@XZ) referenced in function "private: virtual void __cdecl EventTest_TestEventFiresAndPassesArgs_Test::TestBody(void)" (?TestBody@EventTest_TestEventFiresAndPassesArgs_Test@@EEAAXXZ)
2>..\runCoreUnitTests.exe : fatal error LNK1120: 4 unresolved externals
Any ideas what I'm doing wrong here?
Upvotes: 2
Views: 686
Reputation: 206567
You need functions to be exported using __declspec(dllexport)
when creating a DLL.
You can use such functions from another DLL by declaring those functions using __declspec(dllimport)
.
These work great for regular functions.
However, for class templates and function templates, the templates are instantiated on an as needed basis. They don't get exported to the DLL in which they are defined. Hence, they can't get imported from the DLL either. For this reason, you don't use __declspec(dllexport)
or __declspec(dllimport)
with class templates and function templates.
Upvotes: 3