(我正在使用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 ");
这至少有效,但我不希望将className
,method
和arg
硬编码为"String"
,"removeLeading"
和{ {1}}。
我想知道是否有办法用所有新的类型特征/“元”模板(不知道还有什么叫它们)来解决这个问题,我还没有做过多少工作。 (我看看过去一两年里可用的一些库,它们对我来说几乎看起来像一种不同的语言......)
答案 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 ");
}