cdeclog

[C++11] Variadic template 본문

Dev/C++

[C++11] Variadic template

디클 2014.11.02 00:04

# Variadic template
  - template 에서 타입의 인자를 가변으로 처리 할 수 있는, 새로운 ... 표현식이이 생겼다 

  - http://en.wikipedia.org/wiki/Variadic_template 


  - 그리고 또 다른 설명인 MSDN의 링크 
     
http://msdn.microsoft.com/ko-kr/library/dn439779.aspx

variadic 템플릿 함수를 통합하는 대부분의 구현은 일부 양식의 재귀를 사용하지만 전통적인 재귀와는 약간 다릅니다. 전통적인 재귀는 같은 서명을 사용하여 자신을 호출하는 함수를 포함합니다. (오버로드하거나 템플릿화할 수 있지만 매번 동일한 시그너처가 선택됩니다.) Variadic 재귀에는 다른 수의 인수(거의 항상 줄어듦)를 사용하여 variadic 함수 템플릿을 호출하는 작업이 포함되므로 매번 다른 서명을 사용하지 않게 됩니다. "기본 사례"는 여전히 필요하지만 재귀적 특성은 다릅니다.

그렇다 대부분 재귀로 구현을 하게끔 되어 있고, 메타 프로그램의 기법과 같다.


  - 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 Class
class 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 implementation
 
COMDispatch& 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_


0 Comments
댓글쓰기 폼