如何提取和使用“可变参数”模板参数及其类型? [已关闭]

问题描述 投票:0回答:2

我如何提取和使用 C++ 类的变量模板参数中定义的类型,而不是定义以下类的多个重复项,这些重复项仅按参数的数量和类型进行延迟:

template <typename T1, typename T2>
class SUTTestFixture1
{
public:
    void SetUp(T1 expected, vector<T2> data)
    {
        _expected = expected;
        _data = data;
    }

protected:
    SUT _sutObj;
    vector<T2> _data;
    T1 _expected;
};
template <typename T1, typename T2, typename T3>
class SUTTestFixture2
{
public:
    void SetUp(T1 expected, T2 param, vector<T3> data)
    {
        _expected = expected;
        _param = param;
        _data = data;
    }

protected:
    SUT _sutObj;
    vector<T2> _data;
    T1 _expected;
    T2 _param;
};

我希望能够仅使用带有变量模板参数的单个类:

template <typename... T>
class SUTTestFixture
{
public:
    void SetUp(T... args)
    {
        _expected = args...[0];
        _data = args...[1]; // expected ‘;’ before ‘...’ token
    }

protected:
    SUT _sutObj;
    vector<T...> _data; // XXX
    T... _expected; // error: expected unqualified-id before ‘...’ token
};

SUTTestFixture1
用例:

class TestFixture1 : public SUTTestFixture1<long, long>, public testing::TestWithParam<tuple<long, vector<long>>>
{
public:
    void SetUp() override
    {
        RangeTestFixture1::SetUp(get<0>(GetParam()), get<1>(GetParam()));
    }
    long Function1Test()
    {
        return _sutObj.Function1(_data);
    }
};

SUTTestFixture2
用例:

class TestFixture2 : public SUTTestFixture2<size_t, size_t, size_t>, public testing::TestWithParam<tuple<size_t, size_t, vector<size_t>>>
{
public:
    void SetUp() override
    {
        RangeTestFixture2::SetUp(get<0>(GetParam()), get<1>(GetParam()), get<2>(GetParam()));
    }
    size_t Function2Test()
    {
        return _sutObj.Function2(_param, _data);
    }
};
c++ c++20 variadic-templates
2个回答
4
投票

您不需要所有值的第二个副本,您可以直接继承

Testing::TestWithParam

template <typename R, typename... Args>
class SUTTestFixture : public testing::TestWithParam<std::tuple<R, Args...>>
{
public:
    R Expected() {
        return std::get<0>(this->GetParam());
    }
    template <typename Method>
    R Actual(Method method) {
        // Peel Expected off the front, and then call _sutObj.Method(args...)
        return std::apply([&](auto&&, auto&&... args){ 
            return std::invoke(method, _sutObj, args...); 
        }, this->GetParam());
    }
protected:
    SUT _sutObj;
};

class TestFixture1 : public SUTTestFixture<long, std::vector<long>>
{
};

class TestFixture2 : public SUTTestFixture<size_t, size_t, std::vector<size_t>>
{
};

INSTANTIATE_TEST_SUITE_P(TestFunction1, TestFixture1, testing::Combine(testing::Values(1L), testing::Values(std::vector{1L})));

TEST_P(TestFixture1, Foo) {
    EXPECT_EQ(Expected(), Actual(&SUT::Function1));
}

INSTANTIATE_TEST_SUITE_P(TestFunction2, TestFixture2, testing::Combine(testing::Values(1uz), testing::Values(1uz), testing::Values(std::vector{1uz})));

TEST_P(TestFixture2, Foo) {
    EXPECT_EQ(Expected(), Actual(&SUT::Function2));
}

作为替代方案,您可以为每个测试夹具的参数定义一个结构体,而不必担心

std::tuple
s

struct TestFixture1Params
{
    long expected; 
    std::vector<long> data; 
};
class TestFixture1 : public testing::TestWithParam<TestFixture1Params>
{
public:
    long Function1Test()
    {
        return _sutObj.Function(GetParam().data);
    }
protected:
    SUT _sutObj;
};

struct TestFixture2Params
{ 
    size_t expected;
    size_t param;
    std::vector<size_t> data; 
};
class TestFixture2 : public testing::TestWithParam<TestFixture2Params>
{
public:
    size_t Function2Test()
    {
        return _sutObj.Function(GetParam().param, GetParam().data);
    }
protected:
    SUT _sutObj;
};

1
投票

您必须从模板参数列表中独立提取第一个 n-1 和最后一个参数,然后将它们写入自变量。 如果您首先接收向量参数,然后接收其余参数,那么这个过程会容易得多。但就目前情况而言,您必须使用辅助类型进行一些模板参数列表操作。

template <typename... Ts>
struct LastArg {};
template <typename T0, typename T1, typename... Ts>
struct LastArg<T0, T1, Ts...> : LastArg<T1,Ts...> {
};
template <typename T>
struct LastArg<T> {
  using type = T;
};

template <typename T0, typename AL>
struct PrependToList{};
template <typename T0, typename... Ts>
struct PrependToList<T0, std::tuple<Ts...>> {
  using type = std::tuple<T0, Ts...>;
};

template <typename... Ts>
struct DropLast {};
template <typename T0, typename T1, typename... Ts>
struct DropLast<T0,T1,Ts...> {
  using type = typename PrependToList<T0, typename::DropLast<T1,Ts...>::type>::type;
};
template <typename T>
struct DropLast<T> {
  using type = std::tuple<>;
};

template <typename... T>
class SUTTestFixture {
public:
    using Data = std::vector<typename LastArg<T...>::type>;
    using Expect = typename DropLast<T...>::type;

    void SetUp(Expect expect, Data data)
    {
        _expected = expect;
        _data = data;
    }
    template <typename... U>
    requires (std::is_same_v<typename DropLast<U...>::type,Expect> && std::is_same_v<typename LastArg<U...>::type, Data>)
    void SetUp(U... us) {
        SetUp(slice(std::tuple{us...}, std::make_index_sequence<sizeof...(us)-1>{}), std::get<sizeof...(U)-1>(std::tuple{us...}));
    }

protected:
    //SUT _sutObj;
    Data _data;
    Expect _expected;

    template <typename Tup, std::size_t... I>
    auto slice(Tup tup, std::index_sequence<I...>) {
        return std::tuple{std::get<I>(tup)...};
    }
};

这是一个演示

© www.soinside.com 2019 - 2024. All rights reserved.