【问题标题】:Defining a variadic template type with unknown argument list as std::map value type将具有未知参数列表的可变参数模板类型定义为 std::map 值类型
【发布时间】:2018-01-26 16:14:33
【问题描述】:

我不确定这是否可能在过去 3 个小时内没有任何有意义的方法来实现。

我有一个这样的可变参数模板:

template<typename... Ts>
class xunit_test_item {
public:
    template<typename F, typename... Args>
    xunit_test_item(const std::string& name, F&& func, Args&&... args)
    : m_name(name)
    , m_f(std::forward<F>(func))
    , m_args(std::forward<Args>(args)...)
    {
    }

    // ...

private:
    const std::string m_name;
    std::function<void (Ts...) > m_f;
    std::tuple<Ts...> m_args;
};

上面的模板包装了一个测试项目,它基本上是一个函子的持有者,带有传递的参数以及一个用于命名该项目的字符串。

该类型的对象必须作为值类型存储到 std::map 中。这是在一个类中处理的,该类包含一个std::map 容器作为成员、一个添加 xunit_test_item 项目的注册方法和一个 run 方法,该方法遍历映射并执行所有存储的 xunit_test_item 存储函数。

这个类看起来基本上是这样的:

class unit_test {

    // Define a map type of xunit_test_items
    template<typename ...Ts>
    using testitem_map_t = std::map<size_t, xunit_test_item<Ts...>>;

public:
    unit_test() = delete;
    unit_test(const std::string& group_name)
    : m_tm()
    , m_group_name(group_name)
    , m_test_id(0)
    {

    }
    // Register an functor item
    template<typename F, typename ...Args>
    void add_test(const std::string& name, F&& fun, Args&& ...args) {
        m_tm.emplace(++m_test_id, xunit_test_item<Args...>(
            name, std::forward<F>(fun), std::forward<Args>(args)...));
    }

    void run() 
    {
        // ...

        for (auto& t : m_tm) {
            // ...
            t.second->run();
            // ...
        }
    }

private:
    testitem_map_t m_tm; // <-- Compiler correctly reports an error
    std::string m_group_name;
    size_t m_test_id;
};

我遇到的问题是找到正确的语法/不同的方法来将xunit_test_item 定义为m_tm 映射成员的值类型。上面的代码没有编译 - 毫无疑问 - 因为我必须为 m_tm 成员指定一个参数列表,其类型为 testitem_map_t。我不知道如何定义该类型并正确声明 m_tm 成员。此时参数列表是未知的。 也许我上面的课程有设计问题,我想知道是否有或多或少简单的方法来实现这一点。我也可能完全看不到一个简单的解决方案。

很乐意学习并得到好的建议。提前致谢!

更新

新鲜的一天,新鲜的尝试和一杯咖啡......

我使用 2 级 lambda 重构了 add_test 方法的主体,这似乎到目前为止有效。作为测试函数(实际的测试用例),我可以添加 lambdas、带有任意参数或根本没有参数的函数指针。

我不知道这是否是最好的解决方案,或者是否可以以更好的方式编码,也许有人想评论这个话题。也是我不输入这个作为答案的原因。我最初的问题是希望学习一些 C++14 糖作为解决方案……如果可能的话。

这是第一个粗略测试的版本...

template<typename F, typename ...Args>
void add_test(const std::string& name, F&& fun, Args&& ...args) {
    std::function<void(Args...)> fun_xargs =
        [name, fun](auto&&... args) {
            std::cout << "  inner lambda\n";
            auto x = xunit_test_item<Args...>(name, fun, std::forward<Args>(args)...);
            x.run();
        };

    std::function<void()> fun_void = 
        [=] {
            std::cout << "outer lambda\n";
            fun_xargs(args...);
        };

    m_tm.emplace(++m_test_id, fun_void);
}

【问题讨论】:

  • run 包装在 lambda 中,并将其作为 std::function&lt;void()&gt; 存储在地图中
  • 注意:xunit_test_item::xunit_test_itemArgs... 参数只能解析为可转换为Ts... 的包,因此您最好在此处使用Ts...
  • @caleth:写得好!
  • @vu1p3n0x:我不太明白,当你的评论得到支持时,我很好奇……我插入的是xunit_test_item,而不是函数。
  • 您的xunit_test_item 会删除函子的类型,但不会删除参数类型。您需要删除两者。

标签: c++ types c++14 variadic-templates variadic-functions


【解决方案1】:

看来你只需要:

class unit_test {
public:
    unit_test() = delete;
    unit_test(const std::string& group_name)
    : m_tm(), m_group_name(group_name)
    {

    }

    // Register an functor item
    void add_test(const std::string& name, std::function<void()> f) {
        m_tm.emplace(name, std::move(f));
    }

    void run() 
    {
        // ...
        for (const auto& t : m_tm) {
            // ...
            t.second->run();
            // ...
        }
    }

private:
    std::map<std::string, std::function<void()>> m_tm;
    std::string m_group_name;
};

【讨论】:

  • 看起来像我的第一个版本,但我对其进行了扩展以将测试函数、其任意参数、测试名称保存在单独的模板 xunit_test_item 中。我只是展示了构造函数,它还有更多方法以及传入函数的执行方法run。顺便说一句,该设计类似于stackoverflow.com/questions/16868129/… 的公认答案。我目前的尝试是 add_test 方法中的 lambda 包装器,以添加可变参数 xunit_test_item,但我不知道它是否能解决我的问题。
  • 您希望xunit_test_item 提供哪些功能,尤其是您希望通过m_tm 访问的功能?
  • 方法真的不多。函数/函子作曲家,一个run 方法(实际上这是在unit_test::run 内部执行的run),最后但并非最不重要的一些getter,测试用例名称字符串和一个字符串化参数getter(args to string)使用std::tuple 成员。我很高兴将所有这些都集中在一个模板中,这也是未来增强功能的好地方,例如执行时间等。但正如我所见,将这些对象存储到地图中是一场噩梦。并且 lambda 尝试给了我论证推导问题...
猜你喜欢
  • 1970-01-01
  • 2020-11-16
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-09
相关资源
最近更新 更多