创建一个编译时键到类型映射,通过调用可变参数函数来填充
Create a compile time key-to-type map which is filled by calls to a variadic function
是否可以在编译时创建键->类型映射,在调用可变参数函数的实例时添加每个键值?
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->type 或 int->type(以可行者为准)但是当函数 foo 被调用时映射被填充,所以它不是要求事先了解代码和参数。
这可能吗?要么通过boost/preprocessor/template编程?
编辑:
如前所述,CODEMAP 是存储代码 -> 类型信息的映射。
在运行时,reader 块(比如 R)将通过 foo() 读取消息 stored/processed 并根据消息开头的代码进行解析。代码总是固定大小(4 个字符或 1 个整数)。
是同一个翻译单位。
编辑:
所以这是交易:
生产者:将数据写入 FIFO 队列(关键代码热路径)-> 消费者线程从队列中读取并处理信息。
伪代码如下:
制作人:
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>());
}
}
}
}
您可以在 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);
}
然后您可以添加另一个模板以根据消息的代码找到相关的处理步骤以应用到您的队列...为此我们必须 "cheat" 一点:因为我们只能在运行时需要代码,我们不能立即分支处理,我们需要使用 "template switch" 技巧。如下图所示:
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 上找到一个带有虚拟 YourQueue
和 process()
实现的 运行 示例。
这样就解决了消费者部分。您可以通过在 pack_processor
专业化和通用 writeToQueue
方法中添加相关方法来类似地解决生产者部分,该方法将使用与我们刚才相同的 Template Switch 技巧看到了。
所以,我尝试使用多态性。它似乎通过将派生的格式化消息存储到队列中来工作。出队时,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());
}
}
}
是否可以在编译时创建键->类型映射,在调用可变参数函数的实例时添加每个键值?
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->type 或 int->type(以可行者为准)但是当函数 foo 被调用时映射被填充,所以它不是要求事先了解代码和参数。
这可能吗?要么通过boost/preprocessor/template编程?
编辑: 如前所述,CODEMAP 是存储代码 -> 类型信息的映射。 在运行时,reader 块(比如 R)将通过 foo() 读取消息 stored/processed 并根据消息开头的代码进行解析。代码总是固定大小(4 个字符或 1 个整数)。
是同一个翻译单位。
编辑: 所以这是交易:
生产者:将数据写入 FIFO 队列(关键代码热路径)-> 消费者线程从队列中读取并处理信息。
伪代码如下:
制作人:
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>());
}
}
}
}
您可以在 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);
}
然后您可以添加另一个模板以根据消息的代码找到相关的处理步骤以应用到您的队列...为此我们必须 "cheat" 一点:因为我们只能在运行时需要代码,我们不能立即分支处理,我们需要使用 "template switch" 技巧。如下图所示:
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 上找到一个带有虚拟 YourQueue
和 process()
实现的 运行 示例。
这样就解决了消费者部分。您可以通过在 pack_processor
专业化和通用 writeToQueue
方法中添加相关方法来类似地解决生产者部分,该方法将使用与我们刚才相同的 Template Switch 技巧看到了。
所以,我尝试使用多态性。它似乎通过将派生的格式化消息存储到队列中来工作。出队时,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());
}
}
}