简体   繁体   English

映射可变参数模板参数

[英]Map Variadic Template Arguments

In the following code, I would like to find a more generic way of calling GenericPublish__Advertise(), which takes a variadic template list. 在下面的代码中,我想找到一种更通用的调用GenericPublish__Advertise()的方式,该方式采用可变模板列表。 What could I do to improve it? 我该怎么做才能改善它?

I would like to map topics to a publisher of a specific type: 我想将主题映射到特定类型的发布者:

  • topic[0] -> publisher[0] 主题[0]->发布者[0]
  • topic[1] -> publisher[1] 主题[1]->发布者[1]
  • so forth 以此类推

While the code works ok, I need to manually write the templated versions of GenericPublish__Advertise() and manually map topics[i] to publishers. 虽然代码可以正常运行,但我需要手动编写GenericPublish__Advertise()的模板版本,并将主题[i]手动映射到发布者。 I would like to somehow generalize the implementation of GenericPublish__Advertise(). 我想以某种方式概括GenericPublish__Advertise()的实现。

Many thanks in advance. 提前谢谢了。

Code: 码:

    #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;
    }

The typical approach here is to use the index sequence trick. 这里的典型方法是使用索引序列技巧。 You take a parameter pack of types, construct an index sequence of the same size, and then iterate over both: 您采用类型的参数包,构造相同大小的索引序列,然后遍历两个:

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
}

with the separate implementation doing: 使用单独的实现来做:

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))), ...);
}

See this answer for explanation of that pattern. 有关模式的说明,请参见此答案


Note that using GenericPublish__Advertise is UB: names with double underscores are reserved. 请注意,使用GenericPublish__Advertise是UB:保留带双下划线的名称。

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...>{});
}

There's two tricks here which are pretty standard when you want to do some kind of indexing over a variadic pack. 当您要对可变参数包进行某种索引时,这里有两个技巧很标准。 The first thing is that you delegate to an implementation function, passing it all the arguments, plus this std::index_sequence type. 第一件事是您委托给实现函数,将所有参数以及该std::index_sequence类型传递给它。 This lets the implementation function deduce a pack of integers which will be numbered 0 to N-1 a pack of size N. Second, you initialize an unused dummy array, and use the comma operator to discard the return (or lack thereof) of whatever you are doing, and simply return 0. 这使实现函数可以推导一包整数,将其编号为0到N-1以及一个大小为N的包。其次,您可以初始化一个未使用的伪数组,并使用逗号运算符丢弃任何返回值(或缺少返回值)您正在做,只需返回0。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM