创建编译时键到键映射,该映射由对可变参数函数

时间:2016-07-25 09:40:40

标签: c++ templates boost variadic-templates rtti

是否可以在编译时创建key->类型映射,并在调用可变参数函数的实例时添加每个键值?

template <typename T, typename ... Args>
void writeToQueue(Args... args) {
    //Do something with args.
    // Insert to map. something akin to:
    // CODEMAP[T] = Args...
    // T -> Args... mapped when foo<T,Args...> is called.
}

或者

template <int code, typename ... Args>
void writeToQueue(Args... args) {
    //Do something with args.
    // Insert to map. something akin to:
    // CODEMAP[code] = Args...
    // code -> Args... mapped when foo<code,Args...> is called.
}

在上面,要求是有一个CODEMAP,它映射了type-&gt; type或int-&gt; type(取决于哪个)但是在调用函数foo时填充了map,所以它不是事先要求了解代码和args。

这一切都可能吗?通过升压/预处理器/模板编程?

编辑: CODEMAP如上所述,存储代码的地图 - &gt;类型信息。 在运行时,读取器块(比如R)将读取由foo()存储/处理的消息,并根据消息开头的代码进行解析。代码总是固定大小(4个字符或1个int)。

它是相同的翻译单位。

编辑: 所以这就是交易:

生产者:将数据写入FIFO队列(关键代码热路径) - &gt;消费者线程从队列中读取和处理信息。

伪代码如下:

制片:

void Producer::run() {
    // This guy shouldn't worry about the type of data being written.
    // So, encapsulating data into structs and passing it to queue is
    // out of question.
    writeToQueue<Code1>(1,2,"123123",'a', 3.1416);
    writeToQueue<Code2>(4,1,'b');

    template <int Code, typename ...Args>
    void writeToQueue(Args... args) {
     queue.insert(args...);
     // Need code to args... mapping. So, decided to create static
     // instantiation of a formatspecifier class.
     static formatspecifier<Code, args...> f{};
    }

    // To encode the type information to be used in run time.
    template <int Code, typename ... Args>
    class formatspecifier{
       formatspecifier() {
          global::codemap[Code] = encodeTypeInfo<Args...>();
       }
    };
}

消费者:

void Consumer::readfromQueue() {
    while(true) {
      if (queue.dataAvailable()) {
        const auto code = queue.getCode();
        // get encoded type info format from global::codemap map.
        const auto fmt = global::codemap[code];
        for (int i=0; i < fmt.len; i++) {
           // I am unsure how this part should look.
           process<fmt[0]::type>(queue.getData<fmt[0]::type>());
        } 
      }  
    }
 }

2 个答案:

答案 0 :(得分:1)

您可以在code上模拟结构,而不是拥有地图,如下所示:

enum Codes {
    BEGIN_CODE = 0,
    Code1,
    Code2,
    NB_CODES
};

template <typename ... Args>
struct param_pack {
    // Alternatively you could also use std::tuple?!
};

template <Code code>
struct code_info;

// You still have to manually define this at some point...
// For all your different messages...
template <>
struct code_info<Code1> {
    typedef param_pack<int, double, double> args_t;
};

template <>
struct code_info<Code2> {
    typedef param_pack<int, float, float, long> args_t;
}

检查第一步,我们在某处提供了不同消息代码的类型信息。现在,我们如何使用这些信息处理它们?现在是一些模板魔法的时候了:

namespace details {
template <typename ArgPack>
struct pack_processor;

template <typename T, typename ... Args>
struct pack_processor<param_pack<T, Args...>> {
    static void process_pack(YourQueue& queue) {
        process<T>(queue.getData<T>());
        pack_processor<param_pack<Args...>>::process_pack(queue);
    }
};

template <typename T>
struct pack_processor<param_pack<T>> {
    static void process_pack(YourQueue& queue) {
        process<T>(queue.getData<T>());
    }
};
} // namespace details

template <Code code>
process_message(YourQueue& queue) {
    details::pack_processor<typename code_info<code>::args_t>::process_pack(queue);
}

然后你可以添加另一个模板来根据消息的代码找到要应用于队列的相关处理步骤......为此,我们必须“作弊”一点:因为我们只能在运行时获得所需的代码,我们不能立即分支处理,我们需要使用“模板切换”技巧。这说明如下:

namespace details {
// This is not static:
// you can't have static polymorphism when you get information from runtime...
template <Code ref_code>
void process_data_with_code(Code code, YourQueue& queue) {
    // We found the good code:
    if (code == ref_code) {
        // We retrieve the static information
        // and run the appropriate process_pack specialization -> this is static
        process_message<ref_code>(queue);
    } else {
        process_data_with_code<static_cast<Code>(ref_code-1)>(code, queue);
    }
}

template <>
void process_data_for_code<BEGIN_CODE>(Code code, YourQueue& queue) {
    std::cout << "Code not found..." << std::endl;
}

} // namespace details

void process_data(Code code, YourQueue& queue) {
    process_data_for_code<static_cast<Code>(NB_CODE-1)>(code, queue);
}

您可以在Coliru上找到一个正在运行的示例,其中包含虚拟YourQueueprocess()实现。

这解决了消费者的角色。您可以通过在pack_processor特化内添加相关方法和使用我们刚看到的相同类型的模板切换技巧的通用writeToQueue方法来类似地解决生产者部分。< / p>

答案 1 :(得分:0)

所以,我尝试使用多态。它似乎通过将派生的格式化消息存储到队列中来工作。在排队时,vptr应该指向process()的正确实现。

class Message {
   virtual void process() = 0;
}

template <typename... Args>
class FormattedMessage : public Message {
   std::tuple<Args...> data;
   //Specialize process function for each formatted message.
   void process() {
      //As now I have the tuple, I can easily iterate/process it.
   }
}

制片:

template <typename ...Args>
void Producer::writeToQueue(Args... args) {
    using fmttype = FormattedMessage<Args...>;
    this->queue.push<fmttype>(args...);
}

消费者:

void Consumer::readfromQueue() {
    while(true) {
      if (queue.dataAvailable()) {
          this->queue.template front<Message>().process();
          this->queue.pop(this->queue.template front<Message>().size());
      }  
    }
 }