将“存储的”可变参数模板类型解压缩为模板参数

时间:2017-09-06 20:51:58

标签: c++ templates variadic-templates

我正在尝试编写模拟以用于单元测试。要做到这一点,我需要镜像我无法改变的库的实际实现。这是一个非常简单的例子:

LibraryCodeICantChange.h:

struct IFoo {};
struct IBar {};

template<typename... I>
struct implements {};

template<typename... I>
struct mocks {};

struct CRealSingle : implements<IFoo> {};
struct CRealMulti : implements<IFoo, IBar> {};

简单地说,我的嘲讽就是这样:

#include "LibraryCodeICantChange.h"

struct CMockSingle : mocks<IFoo> {};
struct CMockMulti : mocks<IFoo, IBar> {};

但是,我想从真实中推断出接口列表,而不是重复它。我让它适用于单个界面,但我需要一组可变的接口模板参数。我将可变参数类型存储到一个名为pack的结构中,但我不知道如何解压缩这些类型:

#include "LibraryCodeICantChange.h"

template<typename... I>
struct pack {};

// just declared, not defined: only used for typing
template<typename... I>
pack<I...> steal_real_params(implements<I...>*);

template<typename C>
using steal_real_params_t = decltype(steal_real_params(std::declval<C*>()));

template<typename C>
struct MockBasedOnReal: mocks<steal_real_params_t<C>... /* how to unpack? */> {};
// error C3546: '...': there are no parameter packs available to expand

struct CMockSingle : MockBasedOnReal<CRealSingle> {}; // error while 'MockBasedOnReal<CRealSingle>' being compiled
struct CMockMulti : MockBasedOnReal<CRealMulti> {}; // error while 'MockBasedOnReal<CMockMulti>' being compiled

我正在尝试做什么?如果是这样,怎么样?

1 个答案:

答案 0 :(得分:1)

所以,正如我理解你的问题,它归结为这个起点:

struct CRealMulti : implements<IFoo, IBar> {};

并且您希望有一个模板,您可以将此CRealMulti类提供给,然后能够派生从mocks<Ifoo, IBar>继承的内容。用gcc 7.1.1测试:

#include <utility>

struct IFoo {};
struct IBar {};
struct IBaz {};

template<typename... I>
struct implements {};

template<typename... I>
struct mocks {};

struct CRealSingle : implements<IFoo> {};
struct CRealMulti : implements<IFoo, IBar> {};

// Given a subclass of implements<I...>, extract it:

template<typename ...I>
implements<I...> return_implements(implements<I...> &&);

// Specialization to extract the parameter pack:

template<typename T> struct implements_to_mocks;

template<typename ...I>
struct implements_to_mocks<implements<I...>> {

    typedef mocks<I...> mocks_t;
};

// All the hard work is here:

template<typename C> struct to_mock {

    typedef decltype(return_implements(std::declval<C &&>())) implements_t;

    typedef typename implements_to_mocks<implements_t>::mocks_t type;
};

// to_mock_t provides a convenient shortcut

template<typename T>
using to_mock_t=typename to_mock<T>::type;

// The end result:
//    
// to_mock_t<CRealMulti> is an alias for mocks<IFoo, IBar>, ready
// and waiting to be inherited from.

void foo()
{
    typedef std::enable_if<std::is_same<
        to_mock_t<CRealMulti>, mocks<IFoo, IBar>>::value>::type t;
}