映射Variadic模板参数

时间:2017-08-23 13:47:10

标签: c++ c++11 templates c++14 variadic-templates

在下面的代码中,我想找到一种更通用的方法来调用GenericPublish__Advertise(),它采用可变参数模板列表。我该怎么做才能改善它?

我想将主题映射到特定类型的发布商:

  • 主题[0] - >出版商[0]
  • 主题[1] - >出版商[1]
  • 所以

虽然代码工作正常,但我需要手动编写GenericPublish__Advertise()的模板化版本,并手动将主题[i]映射到发布者。我想以某种方式概括GenericPublish__Advertise()的实现。

非常感谢提前。

代码:

    #include <iostream>
    #include <memory>
    #include <typeinfo>
    #include <vector>

    class AdvertiseOptionsBase {
    public:
      virtual const std::type_info &GetType() = 0;
    };

    template <typename TSend> 
    class AdvertiseOptions : public AdvertiseOptionsBase {
    public:
      AdvertiseOptions(TSend opt) : opt_(opt) {}
      const std::type_info &GetType() { return typeid(opt_); }

    private:
      TSend opt_;
    };

    class Publisher {
    public:
      Publisher(const std::string &topic) : topic_(topic) {}
      const std::string &GetTopic() const { return topic_; }
      template <typename TSend>
      void SetOptions(const AdvertiseOptions<TSend> &opt) {
        options_ = std::make_unique<AdvertiseOptions<TSend>>(opt);
      }
      const std::unique_ptr<AdvertiseOptionsBase> &GetOptions() const {
        return options_;
      }

    private:
      std::string topic_;
      std::unique_ptr<AdvertiseOptionsBase> options_;
    };

    class Node {
    public:
      template <typename TSend>
      Publisher advertise(std::string topic) {
        Publisher publisher(topic);
        TSend option;
        AdvertiseOptions<TSend> options(option);
        publisher.SetOptions<TSend>(options);
        return publisher;
      }
    };

    template <typename TSend1, typename TSend2>
    void GenericPublish__Advertise(Node &node, std::vector<Publisher> &publishers,
                                   const std::vector<std::string> &topics) {
      publishers.push_back(node.advertise<TSend1>(topics.at(0)));
      publishers.push_back(node.advertise<TSend2>(topics.at(1)));
    }
    template <typename TSend1, typename TSend2, typename TSend3>
    void GenericPublish__Advertise(Node &node, std::vector<Publisher> &publishers,
                                   const std::vector<std::string> &topics) {
      publishers.push_back(node.advertise<TSend1>(topics.at(0)));
      publishers.push_back(node.advertise<TSend2>(topics.at(1)));
      publishers.push_back(node.advertise<TSend3>(topics.at(2)));
    }

    template <typename... TSend>
    class GenericPublish {
    public:
      GenericPublish(const std::vector<std::string> &topics) {
        GenericPublish__Advertise<TSend...>(node_, publishers_, topics);
      }
      void PrintInfo() {
        for (const auto &publisher : publishers_) {
          std::cout << publisher.GetTopic() << " -----> "
                         << (publisher.GetOptions()->GetType()).name() << std::endl;
        }
      }

    protected:
      Node node_;
      std::vector<Publisher> publishers_;

    private:
    };

    int main() {
      std::vector<std::string> topics({"topic_int", "topic_double"});
      GenericPublish<int, double> o(topics);
      o.PrintInfo();
      return 0;
    }

2 个答案:

答案 0 :(得分:3)

这里的典型方法是使用index sequence技巧。您获取一个类型的参数包,构造一个相同大小的索引序列,然后迭代两者:

template <typename... TSends> // <== pack of types
void GenericPublishAdvertise(Node &node, std::vector<Publisher> &publishers,
                             const std::vector<std::string> &topics)
{
  GenericPublishAdvertiseImpl<TSends...>(node, publishers, topics,
      std::index_sequence_for<TSends...>()); // <== index sequence creation
}

单独的实现:

template <typename... TSends, size_t... Is>
void GenericPublishAdvertiseImpl(Node &node, std::vector<Publisher> &publishers,
    const std::vector<std::string> &topics, std::index_sequence<Is...> )
{
    // since this is C++14
    using swallow = int[];
    (void)swallow{0,
        (void(publishers.push_back(node.advertise<TSends>(topics.at(Is)))), 0)...
        };

    // in C++17, that's just
    (publishers.push_back(node.advertise<TSends>(topics.at(Is))), ...);
}

有关该模式的说明,请参阅this answer

请注意,使用GenericPublish__Advertise是UB:保留带有双下划线的名称。

答案 1 :(得分:2)

template <class ... TSends, std::size_t ... Is>
GenericPublish__Advertise_impl(Node &node, std::vector<Publisher> &publishers,
                               const std::vector<std::string>& topics, std::index_sequence<Is...>)
{
    (void)int x[] = {(publishers.push_back(node.advertise<TSends>(topics.at(Is))), 0)...};
} 


template <class ... TSends>
GenericPublish__Advertise((Node &node, std::vector<Publisher> &publishers,
                               const std::vector<std::string>& topics)
{
    GenericPublish__Advertise_impl(node, publishers, topics, std::index_sequence_for<TSends...>{});
}

当你想对可变参数包进行某种索引时,这里有两个技巧非常标准。第一件事是你委托给一个实现函数,传递它所有的参数,加上这个std::index_sequence类型。这让实现函数推导出一组整数,这些整数将编号为0到N-1,一个大小为N的包。其次,你初始化一个未使用的虚拟数组,并使用逗号运算符来丢弃任何返回(或缺少)你正在做,只需返回0。