clang++:候选函数[带有callable_t = void(int,double),args_t =< int,double>]不可行:期望第三个参数的rvalue

发布于 2025-01-18 17:23:48 字数 3470 浏览 4 评论 0原文

我有一个测试框架,我需要捕获对通用函数(测试套件)的调用并稍后执行它。

为此,我使用带有参数包的类模板,并将参数存储在元组中。

在 GCC 10 和 GCC 11 中一切都构建并运行良好,但在 clang 中失败。

代码如下所示:

  template <typename Callable_T, typename... Args_T>
  class test_suite : public test_suite_base
  {
  public:
    test_suite (const char* name, Callable_T&& callable,
                Args_T&&... arguments);

    virtual ~test_suite () override;

    virtual void
    run (void) override;

  protected:
    Callable_T&& callable_;
    std::tuple<Args_T...> arguments_;
  };

  template <typename Callable_T, typename... Args_T>
  test_suite<Callable_T, Args_T...>::test_suite (const char* name,
                                                 Callable_T&& callable,
                                                 Args_T&&... arguments)
      : test_suite_base{ name }, callable_{ std::forward<Callable_T> (
                                     callable) },
        arguments_{ std::forward<Args_T> (arguments)... }
  {
    runner.register_test_suite (this);
  }

  template <typename Callable_T, typename... Args_T>
  test_suite<Callable_T, Args_T...>::~test_suite ()
  {
  }

  template <typename Callable_T, typename... Args_T>
  void
  test_suite<Callable_T, Args_T...>::run (void)
  {
    current_test_suite = this;

    begin_test_suite ();
    std::apply (callable_, arguments_);
    end_test_suite ();
  }

完整代码可从 GitHub

该类实例化为:

static void
ts_args (int iv, double fv)
{
  using namespace micro_os_plus::micro_test_plus;

  test_case ("args", [&] {
    expect (eq (iv, 42)) << "iv is 42";
    expect (eq (fv, 42.0)) << "fv is 42.0";
  });
}

static micro_os_plus::micro_test_plus::test_suite ts_3
    = { "Args const", ts_args, 42, 42.0 };

static int n = 42;
static micro_os_plus::micro_test_plus::test_suite ts_4
    = { "Args vars", ts_args, n, 42.0 };

第一次调用(使用常量)没问题;第二次调用(使用变量)失败:

/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/src/sample-test.cpp:320:51: error: no viable constructor or deduction guide for deduction of template arguments of 'test_suite'
static micro_os_plus::micro_test_plus::test_suite ts_4
                                                  ^
/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/include/micro-os-plus/test-suite.h:204:5: note: candidate function [with Callable_T = void (int, double), Args_T = <int, double>] not viable: expects an rvalue for 3rd argument
    test_suite (const char* name, Callable_T&& callable,
    ^
/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/include/micro-os-plus/test-suite.h:201:9: note: candidate function template not viable: requires 1 argument, but 4 were provided
  class test_suite : public test_suite_base

我还尝试使用 test_suite 使用显式类型,但没有帮助。

所以事情比我想象的要复杂,而且似乎使用这种语法只有右值应该匹配(尽管GCC不太严格)。

回顾一下,问题是如何捕获带有可变参数的通用调用(在我的例子中,通过静态构造函数在测试套件自动注册期间发生),存储所有内容并在稍后执行它(当测试套件执行部分时)的测试)。

I have a test framework where I need to capture the call to a generic function (a test suite) and execute it at a later time.

For this I use a class template with a parameter pack and I store the arguments in a tuple.

Everything builds and runs fine with GCC 10 and GCC 11, but fails with clang.

The code looks like this:

  template <typename Callable_T, typename... Args_T>
  class test_suite : public test_suite_base
  {
  public:
    test_suite (const char* name, Callable_T&& callable,
                Args_T&&... arguments);

    virtual ~test_suite () override;

    virtual void
    run (void) override;

  protected:
    Callable_T&& callable_;
    std::tuple<Args_T...> arguments_;
  };

  template <typename Callable_T, typename... Args_T>
  test_suite<Callable_T, Args_T...>::test_suite (const char* name,
                                                 Callable_T&& callable,
                                                 Args_T&&... arguments)
      : test_suite_base{ name }, callable_{ std::forward<Callable_T> (
                                     callable) },
        arguments_{ std::forward<Args_T> (arguments)... }
  {
    runner.register_test_suite (this);
  }

  template <typename Callable_T, typename... Args_T>
  test_suite<Callable_T, Args_T...>::~test_suite ()
  {
  }

  template <typename Callable_T, typename... Args_T>
  void
  test_suite<Callable_T, Args_T...>::run (void)
  {
    current_test_suite = this;

    begin_test_suite ();
    std::apply (callable_, arguments_);
    end_test_suite ();
  }

The full code is available from GitHub.

The class is instantiated with:

static void
ts_args (int iv, double fv)
{
  using namespace micro_os_plus::micro_test_plus;

  test_case ("args", [&] {
    expect (eq (iv, 42)) << "iv is 42";
    expect (eq (fv, 42.0)) << "fv is 42.0";
  });
}

static micro_os_plus::micro_test_plus::test_suite ts_3
    = { "Args const", ts_args, 42, 42.0 };

static int n = 42;
static micro_os_plus::micro_test_plus::test_suite ts_4
    = { "Args vars", ts_args, n, 42.0 };

The first call (using constants) is fine; the second call (with a variable) fails with:

/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/tests/src/sample-test.cpp:320:51: error: no viable constructor or deduction guide for deduction of template arguments of 'test_suite'
static micro_os_plus::micro_test_plus::test_suite ts_4
                                                  ^
/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/include/micro-os-plus/test-suite.h:204:5: note: candidate function [with Callable_T = void (int, double), Args_T = <int, double>] not viable: expects an rvalue for 3rd argument
    test_suite (const char* name, Callable_T&& callable,
    ^
/Users/ilg/My Files/WKS Projects/micro-os-plus.github/xPacks/micro-test-plus-xpack.git/include/micro-os-plus/test-suite.h:201:9: note: candidate function template not viable: requires 1 argument, but 4 were provided
  class test_suite : public test_suite_base

I also tried to use explicit types with test_suite<void (int, double), int, double>, but it did not help.

So things are more complicated than I thought, and it seems that with this syntax only rvalues are expected to match (although GCC is less strict).

To recap, the question is how to capture a generic call with variable arguments (which in my case happens during test suite auto-registration, via static constructors), store everything and execute it at a later moment (when the test suite is executed part of the test).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小女人ら 2025-01-25 17:23:48

通过我的测试的解决方案是仅使构造函数成为模板,并将延迟调用存储为使用std :: bind()创建的函数:

  class test_suite : public test_suite_base
  {
  public:
    template <typename Callable_T, typename... Args_T>
    test_suite (const char* name, Callable_T&& callable,
                Args_T&&... arguments);

    // The rule of five.
    test_suite (const test_suite&) = delete;
    test_suite (test_suite&&) = delete;
    test_suite&
    operator= (const test_suite&)
        = delete;
    test_suite&
    operator= (test_suite&&)
        = delete;

    virtual ~test_suite () override;

    virtual void
    run (void) override;

  protected:
    std::function<void (void)> callable_;
  };

  template <typename Callable_T, typename... Args_T>
  test_suite::test_suite (const char* name, Callable_T&& callable,
                          Args_T&&... arguments)
      : test_suite_base{ name }, callable_{ std::bind (callable,
                                                       arguments...) }
  {
    runner.register_test_suite (this);
  }

  void
  test_suite::run (void)
  {
    // Run the test suite function prepared with std::bin();
    callable_ ();
  }

  test_suite::~test_suite ()
  {
  }

参数包已正确捕获并允许常量,变量,参考和指针:

static void
test_suite_args (int ic, int iv, int& ir, int* ip1, int* ip2)
{
  using namespace micro_os_plus::micro_test_plus;

  test_case ("args", [&] {
    expect (eq (ic, 42)) << "ic is 42";
    expect (eq (iv, 43)) << "iv is 43";
    expect (eq (ir, 44)) << "ir is 44";
    expect (eq (*ip1, 45)) << "*ip1 is 45";
    expect (eq (*ip2, 46)) << "*ip2 is 46";
  });
}

static int in = 43;
static int in44 = 44;
static int& ir = in44;
static int in45 = 45;
static int in46 = 46;
static int* ip2 = &in46;

static micro_os_plus::micro_test_plus::test_suite ts_args
    = { "Args", test_suite_args, 42, in, ir, &in45, ip2 };

A solution that passed my tests was to make only the constructor a template, and store the deferred call as a function created with std::bind():

  class test_suite : public test_suite_base
  {
  public:
    template <typename Callable_T, typename... Args_T>
    test_suite (const char* name, Callable_T&& callable,
                Args_T&&... arguments);

    // The rule of five.
    test_suite (const test_suite&) = delete;
    test_suite (test_suite&&) = delete;
    test_suite&
    operator= (const test_suite&)
        = delete;
    test_suite&
    operator= (test_suite&&)
        = delete;

    virtual ~test_suite () override;

    virtual void
    run (void) override;

  protected:
    std::function<void (void)> callable_;
  };

  template <typename Callable_T, typename... Args_T>
  test_suite::test_suite (const char* name, Callable_T&& callable,
                          Args_T&&... arguments)
      : test_suite_base{ name }, callable_{ std::bind (callable,
                                                       arguments...) }
  {
    runner.register_test_suite (this);
  }

  void
  test_suite::run (void)
  {
    // Run the test suite function prepared with std::bin();
    callable_ ();
  }

  test_suite::~test_suite ()
  {
  }

The parameter pack is correctly captured and allows constants, variable, references and pointers:

static void
test_suite_args (int ic, int iv, int& ir, int* ip1, int* ip2)
{
  using namespace micro_os_plus::micro_test_plus;

  test_case ("args", [&] {
    expect (eq (ic, 42)) << "ic is 42";
    expect (eq (iv, 43)) << "iv is 43";
    expect (eq (ir, 44)) << "ir is 44";
    expect (eq (*ip1, 45)) << "*ip1 is 45";
    expect (eq (*ip2, 46)) << "*ip2 is 46";
  });
}

static int in = 43;
static int in44 = 44;
static int& ir = in44;
static int in45 = 45;
static int in46 = 46;
static int* ip2 = &in46;

static micro_os_plus::micro_test_plus::test_suite ts_args
    = { "Args", test_suite_args, 42, in, ir, &in45, ip2 };
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文