I asked a couple of questions recently on StackOverflow to see if I could consolidate some functions into one by making use of templates. Those questions were:
I had one more function to try and update so I thought I would give it a go myself.
This was the function to update:
void CMSATools::ConvertSAFEARRAY_DATE(SAFEARRAY* psaDates, MeetingDates& rMapMeetingDates)
{
DATE *pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaDates, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaDates, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaDates, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
COleDateTime datNotAvailable(pVals[i]);
DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
rMapMeetingDates[dwDatNotAvailable] = datNotAvailable;
}
hr = SafeArrayUnaccessData(psaDates);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaDates);
if (FAILED(hr))
throw _com_error(hr);
}
MeetingDates
is defined like this:
using MeetingDates = std::map<DWORD, COleDateTime>;
So I created this helper function:
template<>
void CMSATools::to_push_back(const DATE& rItem, MeetingDates& rItems)
{
COleDateTime datNotAvailable(rItem);
DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
rItems[dwDatNotAvailable] = datNotAvailable;
}
And I adjusted my calling code like this:
theApp.MSAToolsInterface().ConvertSAFEARRAY<DATE,MeetingDates>(psaDates, mapMeetingDates);
But when I compile this I now get an error:
5>PublishersDatabaseDlg.obj : error LNK2001: unresolved external symbol "public: static void __cdecl CMSATools::ConvertSAFEARRAY<double,class std::map<unsigned long,class ATL::COleDateTime,struct std::less,class std::allocator<struct std::pair<unsigned long const ,class ATL::COleDateTime> > > >(struct tagSAFEARRAY *,class std::map<unsigned long,class ATL::COleDateTime,struct std::less,class std::allocator<struct std::pair<unsigned long const ,class ATL::COleDateTime> > > &)" (??$ConvertSAFEARRAY@NV?$map@KVCOleDateTime@ATL@@U?$less@K@std@@V?$allocator@U?$pair@$$CBKVCOleDateTime@ATL@@@std@@@4@@std@@@CMSATools@@SAXPAUtagSAFEARRAY@@AAV?$map@KVCOleDateTime@ATL@@U?$less@K@std@@V?$allocator@U?$pair@$$CBKVCOleDateTime@ATL@@@std@@@4@@std@@@Z)
What have I done wrong?
My code so far that won't compile when I make the DATE
... templated call:
template<typename to>
void CMSATools::to_clear(to& rItems)
{
rItems.clear();
}
template<typename from, typename to>
void CMSATools::to_push_back(const from& rItem, to& rItems)
{
rItems.push_back(rItem);
}
template<>
void CMSATools::to_clear(CStringArray& rItems)
{
rItems.RemoveAll();
}
template<>
void CMSATools::to_push_back(const BSTR& rItem, CStringArray& rItems)
{
rItems.Add(rItem);
}
template<>
void CMSATools::to_push_back(const DATE& rItem, MeetingDates& rItems)
{
COleDateTime datNotAvailable(rItem);
DWORD dwDatNotAvailable = EncodeMeetingDate(0, datNotAvailable);
rItems[dwDatNotAvailable] = datNotAvailable;
}
template<typename from, typename to>
void CMSATools::ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems)
{
from* pVals = nullptr;
HRESULT hr = SafeArrayAccessData(psaItems, (void**)&pVals); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
hr = SafeArrayGetLBound(psaItems, 1, &lowerBound);
if (FAILED(hr))
throw _com_error(hr);
hr = SafeArrayGetUBound(psaItems, 1, &upperBound);
if (FAILED(hr))
throw _com_error(hr);
to_clear<to>(rItems);
long cnt_elements = upperBound - lowerBound + 1;
for (int i = 0; i < cnt_elements; ++i) // iterate through returned values
{
to_push_back<from, to>(pVals[i], rItems);
}
hr = SafeArrayUnaccessData(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
else
{
throw _com_error(hr);
}
hr = SafeArrayDestroy(psaItems);
if (FAILED(hr))
throw _com_error(hr);
}
I this this answer to a similarly titles question:
Error when pass std::map as template template argument
And I think it might apply in my case but I am not sure how to implement it. If indeed it is the reason.
My header class has this snippet in it:
template<typename to>
static void to_clear(to& rItems);
template<typename from, typename to>
static void to_push_back(const from& rItem, to& rItems);
template<>
static void to_clear(CStringArray& rItems);
template<>
static void to_push_back(const BSTR& rItem, CStringArray& rItems);
template<>
static void to_push_back(const DATE& rItem, MeetingDates& rItems);
template<typename from, typename to>
static void ConvertSAFEARRAY(SAFEARRAY* psaItems, to& rItems);
static DWORD EncodeMeetingDate(int iMeetingType, COleDateTime datMeeting);
At the top of my header file I have:
#pragma once
#include "DemoPickerDlg.h"
#include <map>
#include <vector>
#ifdef _WIN64
#import "..\\..\\MSAToolsLibrary\\MSAToolsLibrary\\bin\\x64\\Release\\MSAToolsLibrary.tlb" raw_interfaces_only named_guids
#else
#import "..\\..\\MSAToolsLibrary\\MSAToolsLibrary\\bin\\x86\\Release\\MSAToolsLibrary.tlb" raw_interfaces_only named_guids
#endif
using MeetingDates = std::map<DWORD, COleDateTime>;
using ListDiscussionItems = std::list<MSAToolsLibrary::IDiscussionItemPtr>;
using ListStudentItems = std::list<MSAToolsLibrary::IStudentItemPtr>;
using ListDutyHistoryLookupItems = std::list<MSAToolsLibrary::IDutyAssignmentLookupPtr>;
There is a linked question (Why can templates only be implemented in the header file?) provided to me in the comments. But the answer itself didn't make it clear for me. It was the article that it linked to that helped me understand:
To quote:
But in order to understand why things are the way they are, first accept these facts:
- A template is not a class or a function. A template is a “pattern” that the compiler uses to generate a family of classes or functions.
That is what made it clear to me. I moved mode code into the header and it now compiles fine. I found that the easiest resolution.