如何在C++中基于标签运行特定的函数?

如何在C++中基于标签运行特定的函数?,c++,c++11,C++,C++11,我正在编写一个测试框架,它将运行数百个测试。每个测试都是作为一个独立的函数编写的。目前,我让我的主函数逐个运行这些测试。我想做的是一个使用标签的系统。例如,我希望能够运行带有“WiFi”标记的所有测试,或者运行带有“notifications”标记的所有测试 我已经试着查找如何做到这一点,但我能找到的所有文章都在谈论对我没有帮助的东西。也许我不需要标签 不确定这是否重要,但我在Mac上用c++11编写,并且使用VS代码 顺便说一句,我是新的堆栈溢出,所以很抱歉,如果我搞砸了什么。提前谢谢 编辑:

我正在编写一个测试框架,它将运行数百个测试。每个测试都是作为一个独立的函数编写的。目前,我让我的主函数逐个运行这些测试。我想做的是一个使用标签的系统。例如,我希望能够运行带有“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,因为不同标记之间的顺序并不重要。但这几乎不会有任何实际的区别。我不确定我是否同意这更简单。我不确定我是否同意这更简单。