测试成员函数,通过剥离成对的可变宏/模板/函数?

时间:2017-10-25 10:49:04

标签: c++ templates variadic-templates c++17 variadic-macros

(我正在使用catch进行单元测试,遗憾的是它还没有调用生成器来执行此类操作。)

c++17中,有没有办法减少这种情况:

assert(""  == String{""  }.removeLeading(' '));
assert("a" == String{" a").removeLeading(' '));
assert("a" == String("a" }.removeLeading(' '));
assert("a" == String{"a "}.removeLeading(' '));

使用像这样的宏,模板或函数:

#define MACRO(className, method, arg, ...) \
   for(auto [x, y] : { __VA_ARGS }) { \
      assert(x == className{y}.method(arg)); \
   }

因此它更短:

MACRO(String, removeLeading, ' ',
   { "", "" }, {"a", " a"}, {"a", "a"}, {"a", "a "})

// or

MACRO(String, removeLeading, ' ',
   "", "",    "a", " a",    "a", "a",    "a", "a ")

假设所有...个参数“auto”属于同一类型。

基本上对... args的数量没有限制。 (也许可以达到100?)

使用第一个MACRO()给出:unable to deduce 'std::initializer_list<auto>&&' from...(它也不理解语义,因此在,标记上严格分开,但只要它正确放在一起,就不会无所谓。)

使用第二个MACRO()提供:cannot decompose non-array non-class type 'const char*'

尝试模板:

template<typename T>
void TEMP(T a, T b) {
    assert(a == String{ b }.removeLeading(' '));
}

template<typename T, typename... Args>
void TEMP(T a, T b, Args... args) {
    TEMP(a, b);
    TEMP(args...);
}

TEMP("", "",    "a", " ",    "a", "a",    "a", "a ");

这至少有效,但我不希望将classNamemethodarg硬编码为"String""removeLeading"和{ {1}}。

我想知道是否有办法用所有新的类型特征/“元”模板(不知道还有什么叫它们)来解决这个问题,我还没有做过多少工作。 (我看看过去一两年里可用的一些库,它们对我来说几乎看起来像一种不同的语言......)

2 个答案:

答案 0 :(得分:2)

这几乎可行:

for (auto [x,y] : {{"a", "b"}, {"c", "d"}, {"e", "f"}}) {
    foo(x, y);
}

唯一的问题是内部的 braced-init-list 不能自己推断出来。所以我们只需要给编译器一点推力。

using P = std::pair<char const*, char const*>;
for (auto [x,y] : {P{"a", "b"}, {"c", "d"}, {"e", "f"}}) {
    foo(x, y);
}

这很有效。只识别第一个就足够了。或者,对于您的具体示例:

for (auto [exp, arg] : {P{"", ""}, {"a", " a"}, {"a", "a"}, {"a", "a "}}) {
    assert(exp == String(arg).removeLeading(' '));
}

如果你真的想为此编写一个宏,那么在这一点上应该清楚看看如何做到这一点。

答案 1 :(得分:0)

  

我不希望将className,method和arg硬编码为"String""removeLeading"" "

如果我理解你想要什么,下面的结构foo(使用静态方法func())可以给你一个想法(但你必须微调细节)

template <typename C, auto ... Vs>
struct foo
 {
   template <typename R> 
   static void func (R(C::*)(decltype(Vs) const & ...))
    { }

   template <typename R, typename T1, typename T2, typename ... Args>
   static void func (R(C::*m)(decltype(Vs) const & ...),
                     T1 const & a, T2 const & b, Args const & ... args)
    {
      assert( a == (C{b}.*m)(Vs...) );

      func(m, args...);
    }
 };

以下是完整的编译示例(由于removeLeading()未真正实现,因此运行失败;因此abort()失败)

#include <cassert>
#include <string>

// fake String strict, with fake removeLeading() method, to
// demonstrative/compiling purposes 
struct String
 {
   String (std::string const &) 
    { }

   std::string removeLeading (char const &)
    { return { }; }
 };

template <typename C, auto ... Vs>
struct foo
 {
   template <typename R> 
   static void func (R(C::*)(decltype(Vs) const & ...))
    { }

   template <typename R, typename T1, typename T2, typename ... Args>
   static void func (R(C::*m)(decltype(Vs) const & ...),
                     T1 const & a, T2 const & b, Args const & ... args)
    {
      assert( a == (C{b}.*m)(Vs...) );

      func(m, args...);
    }
 };

int main()
 {
   foo<String, ' '>::func(&String::removeLeading,
      "", "",    "a", " ",    "a", "a",    "a", "a ");
 }

- 编辑 -

OP编译器不支持模板非类型参数的auto类型。

因此前面的解决方案(基于强auto)无法工作。

我提出另一种解决方案,C ++ 14兼容,应该可行。

#include <tuple>
#include <string>
#include <cassert>
#include <functional>

// fake String strict, with fake removeLeading() method, to
// demonstrative/compiling purposes 
struct String
 {
   String (std::string const &) 
    { }

   std::string removeLeading (char const &)
    { return { }; }
 };

template <typename C, typename ... Vs, std::size_t ... Is, typename R>
void barH (std::tuple<Vs...> const &, std::index_sequence<Is...> const &,
           R(C::*)(Vs const & ...))
 { }

template <typename C, typename ... Vs, std::size_t ... Is, typename R,
          typename T1, typename T2, typename ... Args>
void barH (std::tuple<Vs...> const & tv,
           std::index_sequence<Is...> const & is, R(C::*m)(Vs const & ...),
           T1 const & a, T2 const & b, Args const & ... args)
 {

   assert( a == (C{b}.*m)(std::get<Is>(tv)...) );

   barH<C>(tv, is, m, args...);
 }

template <typename C, typename ... Vs, typename R, typename ... Args>
void bar (std::tuple<Vs...> const & tv, R(C::*m)(Vs const & ...),
          Args const & ... args)
 { barH<C>(tv, std::make_index_sequence<sizeof...(Vs)>{}, m, args...); }


int main()
 {
   bar<String>(std::make_tuple(' '), &String::removeLeading,
               "", "",    "a", " ",    "a", "a",    "a", "a ");
 }