如何在C++中基于标签运行特定的函数?
我正在编写一个测试框架,它将运行数百个测试。每个测试都是作为一个独立的函数编写的。目前,我让我的主函数逐个运行这些测试。我想做的是一个使用标签的系统。例如,我希望能够运行带有“WiFi”标记的所有测试,或者运行带有“notifications”标记的所有测试 我已经试着查找如何做到这一点,但我能找到的所有文章都在谈论对我没有帮助的东西。也许我不需要标签 不确定这是否重要,但我在Mac上用c++11编写,并且使用VS代码 顺便说一句,我是新的堆栈溢出,所以很抱歉,如果我搞砸了什么。提前谢谢 编辑:如何在C++中基于标签运行特定的函数?,c++,c++11,C++,C++11,我正在编写一个测试框架,它将运行数百个测试。每个测试都是作为一个独立的函数编写的。目前,我让我的主函数逐个运行这些测试。我想做的是一个使用标签的系统。例如,我希望能够运行带有“WiFi”标记的所有测试,或者运行带有“notifications”标记的所有测试 我已经试着查找如何做到这一点,但我能找到的所有文章都在谈论对我没有帮助的东西。也许我不需要标签 不确定这是否重要,但我在Mac上用c++11编写,并且使用VS代码 顺便说一句,我是新的堆栈溢出,所以很抱歉,如果我搞砸了什么。提前谢谢 编辑:
我会在每个函数中使用多个标记。例如,有一个功能需要“WiFi”和“网络”标签,另一个功能需要“触觉”、“通知”和“网络”标签。你的研究之所以失败,是因为标签是一个非常宽泛的通用术语,不能真正描述你在做什么 从根本上说,您试图将两个数据段映射到一起;真的是这样。不管您的标记是整数、枚举成员、字符串、类实例还是其他任何内容 你说过我需要一个新的地图为每个标签,但我不同意。您只需要一个从标记到函数的多重映射,如果您还需要反向查找,最坏的情况下,还需要另一个从函数到标记的多重映射。这些都很琐碎
enum class Tag
{
WiFi,
Haptic,
Network,
Notification
};
using Function = std::function<void()>; // or whatever it is
std::multimap<Tag, Function> tagLookup;
void BindTag(const Tag tag, Function func)
{
tagLookup.emplace(tag, std::move(func)); // I'm using move out of habit
}
void RunFuncsForTag(const Tag tag)
{
auto [start, end] = tagLookup.equal_range(tag);
for (auto it = start; it != end; ++it)
{
const Function& func = it->second;
func();
}
}
int main()
{
BindTag(...);
BindTag(...);
RunFuncsForTag(tag);
}
诚然,如果您不需要大量std::函数的副本,还有一些工作要做,您可以将它们存储在另一个容器中,并使用其他映射来查找存储一次的每个函数,但是这里的基本数据存储原理相当简单。你的研究之所以失败,是因为标签是一个非常宽泛的通用术语,它并不能真正描述你在做什么 从根本上说,您试图将两个数据段映射到一起;真的是这样。不管您的标记是整数、枚举成员、字符串、类实例还是其他任何内容 你说过我需要一个新的地图为每个标签,但我不同意。您只需要一个从标记到函数的多重映射,如果您还需要反向查找,最坏的情况下,还需要另一个从函数到标记的多重映射。这些都很琐碎
enum class Tag
{
WiFi,
Haptic,
Network,
Notification
};
using Function = std::function<void()>; // or whatever it is
std::multimap<Tag, Function> tagLookup;
void BindTag(const Tag tag, Function func)
{
tagLookup.emplace(tag, std::move(func)); // I'm using move out of habit
}
void RunFuncsForTag(const Tag tag)
{
auto [start, end] = tagLookup.equal_range(tag);
for (auto it = start; it != end; ++it)
{
const Function& func = it->second;
func();
}
}
int main()
{
BindTag(...);
BindTag(...);
RunFuncsForTag(tag);
}
诚然,如果您不想要std::函数的大量副本,那么还有一些工作要做,您可以将它们存储在另一个容器中,并使用其他映射来查找存储一次的每个函数,但这里的基本数据存储原则相当简单。您可以使用multimap,但是,您也可以使用一个包含所有测试的容器来保持简单:
#include <vector>
#include <type_traits>
template <class E> auto as_underlying(E e) -> std::underlying_type_t<E>
{ return static_cast<std::underlying_type_t<E>>(e); }
enum class Tag : unsigned {
WiFi = 0x01u,
Network = 0x02u,
Haptic = 0x04u,
Notification = 0x08u,
};
auto operator|(Tag lhs, Tag rhs) -> Tag
{
return Tag{as_underlying(lhs) | as_underlying(rhs)};
}
struct Test
{
using F = bool();
Tag tag;
F* test; // or std::function
bool has_tag(Tag tag_to_check) const
{
return as_underlying(tag) & as_underlying(tag_to_check);
}
bool operator()() const { return test(); }
};
template <class Cont>
auto run_by_tag(const Cont& tests, Tag tag)
{
for (const auto& test : tests)
{
if (test.has_tag(tag))
{
test();
}
}
}
auto test()
{
std::vector<Test> tests{
Test{Tag::WiFi | Tag::Network, [] { return true;}},
Test{Tag::Haptic | Tag::Notification | Tag::Network, [] { return false; }},
Test{Tag::Notification, [] { return true;}}
};
run_by_tag(tests, Tag::WiFi);
run_by_tag(tests, Tag::Notification);
}
您可以使用multimap,但也可以使用一个包含所有测试的容器来保持简单:
#include <vector>
#include <type_traits>
template <class E> auto as_underlying(E e) -> std::underlying_type_t<E>
{ return static_cast<std::underlying_type_t<E>>(e); }
enum class Tag : unsigned {
WiFi = 0x01u,
Network = 0x02u,
Haptic = 0x04u,
Notification = 0x08u,
};
auto operator|(Tag lhs, Tag rhs) -> Tag
{
return Tag{as_underlying(lhs) | as_underlying(rhs)};
}
struct Test
{
using F = bool();
Tag tag;
F* test; // or std::function
bool has_tag(Tag tag_to_check) const
{
return as_underlying(tag) & as_underlying(tag_to_check);
}
bool operator()() const { return test(); }
};
template <class Cont>
auto run_by_tag(const Cont& tests, Tag tag)
{
for (const auto& test : tests)
{
if (test.has_tag(tag))
{
test();
}
}
}
auto test()
{
std::vector<Test> tests{
Test{Tag::WiFi | Tag::Network, [] { return true;}},
Test{Tag::Haptic | Tag::Notification | Tag::Network, [] { return false; }},
Test{Tag::Notification, [] { return true;}}
};
run_by_tag(tests, Tag::WiFi);
run_by_tag(tests, Tag::Notification);
}
听起来你需要一个std::map的映射,这样你就可以迭代映射来获得所有一种类型的标记函数。我曾考虑过使用一个映射,但要做到这一点,我需要为每个标记创建一个新映射,这似乎是一种不雅的方法。我预计有数百个测试,可能有20-30个标签。这是个好主意,但我希望有更好的办法。你可以把你的标签做成一根线。我想我不明白你的意思。如果标记是一个字符串,我仍然会有很多巨大的映射,这将成为一个不断更新的挑战。你想用多个标记标记一个函数,还是每个函数一个标记?听起来你需要一个std::map的映射,这样你就可以迭代映射来获得所有类型的标记函数。我考虑过使用映射,但要做到这一点,我需要为每个标签创建一个新的地图,这似乎是一种不雅观的做法。我预计有数百个测试,可能有20-30个标签。这是个好主意,但我希望有更好的办法。你可以把你的标签做成一根线。我想我不明白你的意思。如果标签是一个字符串,我仍然会有很多巨大的地图,这将成为一个挑战,以保持更新。你想能够标记一个功能与多个标签,还是将是一个标签,每个功能?非常感谢!这有助于我更好地理解我的问题,并给我一个解决方案。我将使用std::unordered_multimap,因为不同标记之间的顺序并不重要。但那几乎不会有任何实际的区别。非常感谢!这有助于我更好地理解我的问题,并给我一个解决方案。我将使用std::unordered_multimap,因为不同标记之间的顺序并不重要。但这几乎不会有任何实际的区别。我不确定我是否同意这更简单。我不确定我是否同意这更简单。