# Variadic template
- template 에서 타입의 인자를 가변으로 처리 할 수 있는, 새로운 ... 표현식이이 생겼다
- http://en.wikipedia.org/wiki/Variadic_template
- 그리고 또 다른 설명인 MSDN의 링크
http://msdn.microsoft.com/ko-kr/library/dn439779.aspx
그렇다 대부분 재귀로 구현을 하게끔 되어 있고, 메타 프로그램의 기법과 같다.
- template <typename...Ty> 라는 형식으로 사용 되며 이렇게 사용된 형식은 가변적으로(0~N개) 파라미터를 받아 드릴수 있다
- 이렇게 받은 파라미터는 재귀함수 형식으로 다시 자기 함수를 호출 (Ty...var) 하게 되며, 파라미터가 앞에서 부터 하나씩 제거 되는 형식으로 다음 함수에 전달 되게 된다
- 그리고 마지막 end 조건되 되는 별도의 비가변 template 함수를 호출함으로서 종료 되게 끔 동작 함
# 예제
- 첫번째로 만들어 본 예제는 vector에 모들 가변 파라미터를 넣어 보는것
- 재귀 개념으로 실행 되며 마지막에 실행 되는 end 역활로 비가변 템플릿 함수가 실행 되고 끝냄
- vector의 성능 향상을 위해 C++11 에 포함된 rvalue 레퍼런스를 이용한 move semantics 를 사용하고 마지막 조건 부터 역으로 push 되는 방식을 사용하여 데이터가 template 전달 역순으로 배열 됨.
- 정상적인 순으로 받아드리게 할려면 별도의 파라미터 vector<T> 로 받아서 넣게 하면 될듯.
- vector를 사용하여 구조상 한개의 타입으로 한정 지을 수 밖에(다른 타입을 대입하면 vector에서 에러 분출..) 없지만 variant 타입을 사용 한다면 문제가 없고, C++11의 구현으로 실제 std::tuple의 경우 Variadic template으로 구현 되어 있음
#include <iostream> #include <vector> #include <string> using namespace std; template <typename Ty> vector<Ty> MakeVector(const Ty& p) { vector<Ty> v; v.push_back(p); return std::move(v); } template <typename Ty, typename...VARS> vector<Ty> MakeVector(const Ty& p, VARS...vs) { vector<Ty> v = MakeVector(vs...); v.push_back(p); return std::move(v); } int main() { auto v = MakeVector(1, 3, 5, 7, 9, 11, 13); for (int &n : v) { cout << n << endl; } }
# 결과 (재귀의 역순으로 push 를 해서 역으로 결과가 나옴)
13 11 9 7 5 3 1
# 응용
- C++에서 COM 을 사용하기 위한 방법 중, 디스패치 인터페이스를 사용하기 위해서는 약간의 수고가 필요한데 그것을 좀 더 편하게 사용하게 사용하기 위한 클래스를 만든 적이 있다. 그 클래스는 동적인 파라미터를 처리하기 위해 40개의 오버로딩으로 처리한 이력이 있음. ( 이것 때문이었을까?, VB 6 의 경우 파라미터 개수가 30개가 MAX 인듯)
- 물론 printf 류 함수에서 사용하는 것을 사용 할 수 있지만, 이것은 타입을 정확히 알아야 하고 안정성 측면에 보장 살 수가 없음.
- 기존 구현은 ATL의 _variant_t 를 사용하여 40개의 오버로딩 까지 구현 했으나, C++11 을 새로운 기능인 Variadic template을 이용하여 2개 오버로딩으로 끝냄.
- _variant_t 특성상 여러 타입을 대입 할 수 있어 vector<_variant_t> 가 템플릿 가변 인수를 처리 할 수 있음
# COMDispatchV.h
// COMDispatch.h: interface for the IDispatch class.//////////////////////////////////////////////////////////////////////#ifndef _DISPATCH_H_20011012_BY_CDECL_#define _DISPATCH_H_20011012_BY_CDECL_#include <comdef.h>#include <vector>#define BYREF(x) GLASS::COMDispatch::ToRefParam((x))namespace GLASS {class COMDispatch{public:typedef std::vector<_variant_t> vec_param;typedef _variant_t PT;// Proxy Classclass Proxy{public:void SetParent(COMDispatch *p) { pDisp_ = p; }void SetPropName(const _bstr_t &bstrName) { bstrName_ = bstrName; }_variant_t operator=(const _variant_t &var) const{pDisp_->PutProperty(bstrName_, var);return var;}operator _variant_t() const {return pDisp_->GetProperty(bstrName_);}_variant_t operator() (){vec_param vecParams;return pDisp_->Call(bstrName_, vecParams);}template <typename...VARS>_variant_t operator() (VARS...param){vec_param vecParams = Param(param...);return pDisp_->Call(bstrName_, vecParams);}private:template <typename Vartype>vec_param Param(Vartype p){vec_param v;v.push_back(p);return std::move(v);}template <typename Vartype, typename...VARS>vec_param Param(Vartype p, VARS...vs){vec_param v = Param(vs...);v.push_back(p);return std::move(v);}private:COMDispatch *pDisp_;_bstr_t bstrName_;};public:COMDispatch() : spDispatch_(NULL) { proxy_.SetParent(this); }COMDispatch(const COMDispatch &disp) : spDispatch_(disp.spDispatch_) { proxy_.SetParent(this); }explicit COMDispatch(const _variant_t &var) : spDispatch_(var) { proxy_.SetParent(this); }COMDispatch(const char *) = delete;COMDispatch(const wchar_t *) = delete;virtual ~COMDispatch() { Release(); }COMDispatch& operator=(const COMDispatch &disp);COMDispatch& operator=(const _variant_t &var);public:_variant_t Call(const _bstr_t &bstrMethodName, vec_param &vecParams);void PutProperty(const _bstr_t &strPropName, const _variant_t &var, bool bRef = false);_variant_t GetProperty(const _bstr_t &strPropName);virtual Proxy& operator[](const _bstr_t &bstrPropName);virtual void CreateObject(const _bstr_t &bstrProgID);virtual void Release();public:virtual void GetIDsOfNames(const _bstr_t &bstrMethodName, DISPID &lDispID);protected:virtual void Invoke(const DISPID lDispID, vec_param &vecParams, _variant_t &varRetVal,const WORD wFlags = DISPATCH_METHOD);public:static _variant_t ToRefParam(_variant_t &var);protected:Proxy proxy_;IDispatchPtr spDispatch_;};//////////////////////////////////////////////////////////////////////// Dispatch implementationCOMDispatch& COMDispatch::operator=(const COMDispatch &disp){if (this != &disp) {spDispatch_ = disp.spDispatch_;proxy_.SetParent(this);}return *this;}COMDispatch& COMDispatch::operator=(const _variant_t &var){spDispatch_ = var;proxy_.SetParent(this);return *this;}void COMDispatch::Release(){if (spDispatch_ != NULL) {spDispatch_.Release();}}void COMDispatch::CreateObject(const _bstr_t &bstrProgID){HRESULT hr = spDispatch_.CreateInstance((LPCSTR)bstrProgID);if (FAILED(hr)) {_com_raise_error(hr);}}_variant_t COMDispatch::Call(const _bstr_t &bstrMethodName, vec_param &vecParams){DISPID lDispID;GetIDsOfNames(bstrMethodName, lDispID);_variant_t varRetVal;Invoke(lDispID, vecParams, varRetVal);return varRetVal;}COMDispatch::Proxy& COMDispatch::operator[](const _bstr_t &bstrPropName){proxy_.SetPropName(bstrPropName);return proxy_;}void COMDispatch::PutProperty(const _bstr_t &strPropName, const _variant_t &var, bool bRef){DISPID lDispID;GetIDsOfNames(strPropName, lDispID);vec_param vecParams;vecParams.push_back(var);_variant_t varRetVal;Invoke(lDispID, vecParams, varRetVal, bRef ? DISPATCH_PROPERTYPUTREF : DISPATCH_PROPERTYPUT);}_variant_t COMDispatch::GetProperty(const _bstr_t &strPropName){DISPID lDispID;GetIDsOfNames(strPropName, lDispID);vec_param vecParams;_variant_t varRetVal;Invoke(lDispID, vecParams, varRetVal, DISPATCH_PROPERTYGET);return varRetVal;}void COMDispatch::GetIDsOfNames(const _bstr_t &bstrMethodName, DISPID &lDispID){BSTR bstrMN = static_cast<BSTR>(bstrMethodName);HRESULT hr = spDispatch_->GetIDsOfNames(IID_NULL, &bstrMN, 1, LOCALE_SYSTEM_DEFAULT, &lDispID);if (FAILED(hr)) {_com_raise_error(hr);}}void COMDispatch::Invoke(const DISPID lDispID, vec_param &vecParams, _variant_t &varRetVal, const WORD wFlags){DISPPARAMS dispparams = { 0 };DISPID dsipid_put = DISPID_PROPERTYPUT;if (DISPATCH_PROPERTYGET != wFlags) {dispparams.cArgs = vecParams.size();dispparams.rgvarg = (vecParams.size() == 0) ? NULL : &vecParams.front();if ((DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF) & wFlags) {dispparams.rgdispidNamedArgs = &dsipid_put;dispparams.cNamedArgs = 1;}}HRESULT hr = spDispatch_->Invoke(lDispID,IID_NULL,LOCALE_USER_DEFAULT,wFlags,&dispparams,DISPATCH_PROPERTYPUT == wFlags ? NULL : &varRetVal,NULL,NULL);if (FAILED(hr)) {_com_raise_error(hr);}}_variant_t COMDispatch::ToRefParam(_variant_t &var){_variant_t varRet;varRet.vt = VT_BYREF | VT_VARIANT;varRet.pvarVal = &var;return varRet;}} // end namespace
#endif // _DISPATCH_H_20011012_BY_CDECL_
'Dev > C++' 카테고리의 다른 글
Catch (A modern, C++-native, header-only, framework for unit-tests, TDD and BDD) (0) | 2015.02.26 |
---|---|
C++ REST SDK (casablanca) 간단 샘플 (0) | 2015.02.17 |
boost 설치 (0) | 2013.11.20 |
boost::asio 클라이언트 소켓 (timeout 기능) (0) | 2010.10.14 |
이미지 변환 모듈 (모바일용, GDI+) (0) | 2010.06.22 |