我正在实现一个任务运行时系统,它为各种类型的用户提供的对象维护缓冲区。此外,所有对象在存储到缓冲区之前都会被包装。由于运行时不知道用户将提供的对象类型,因此Wrapper和Buffer类是模板化的:
template <typename T>
class Wrapper {
private:
T mdata;
public:
Wrapper() = default;
Wrapper(T& user_data) : mdata(user_data) {}
T& GetData() { return mdata; }
...
};
template <typename T>
class Buffer {
private:
std::deque<Wrapper<T>> items;
public:
void Write(Wrapper<T> wd) {
items.push_back(wd);
}
Wrapper<T> Read() {
Wrapper<T> tmp = items.front();
items.pop_front();
return tmp;
}
...
};
现在,运行时系统处理任务,每个任务都在上述缓冲区的子集上运行。因此,每个缓冲区由一个或多个任务操作。这意味着任务必须保持对缓冲区的引用,因为任务可以共享缓冲区。
这是我的问题所在: 1)每个任务都需要保留对多个缓冲区的引用(这个数字在编译时是未知的) 2)缓冲区是不同类型的(基于模板化的Buffer类)。 3)任务需要使用这些引用来访问缓冲区。
没有必要为Buffer类创建一个基类,然后使用基类指针,因为Buffer类中的方法 Write 和 Read 已被模板化,因此不可能是虚拟的。
所以我想把引用保留为void指针,其中Task类看起来像:
class Task {
private:
vector<void *> buffers;
public:
template<typename T>
void AddBuffer(Buffet<T>* bptr) {
buffers.push_back((void *) bptr);
}
template<typename T>
Buffer<T>* GetBufferPtr(int index) {
return some_way_of_cast(buffers[index]);
}
...
};
这个问题是我不知道如何从void指针获取有效指针以访问Buffer。也就是说,我不知道如何保留 buffers [index] 所指向的对象的类型。
你能帮我解决这个问题,还是建议其他解决方案?
编辑:缓冲区只是运行时系统的实现细节,用户不知道它们的存在。
答案 0 :(得分:1)
根据我的经验,当用户类型保存在用户代码中时,运行时系统处理缓冲区不需要担心这些缓冲区的实际类型。用户可以在类型缓冲区上调用操作。
class Task {
private:
vector<void *> buffers;
public:
void AddBuffer(char* bptr) {
buffers.push_back((void *) bptr);
}
char *GetBufferPtr(int index) {
return some_way_of_cast(buffers[index]);
}
...
};
class RTTask: public Task {
/* ... */
void do_stuff() {
Buffer<UserType1> b1; b1Id = b1.id();
Buffer<UserType2> b2; b2Id = b2.id();
AddBuffer(cast(&b1));
AddBuffer(cast(&b2));
}
void do_stuff2() {
Buffer<UserType1> *b1 = cast(GetBufferPtr(b1Id));
b1->push(new UserType1());
}
};
在这些情况下,强制转换属于用户代码。但也许你有一个不同的问题。如果你可以切换到指针,那么Wrapper
类也可能没有必要。
答案 1 :(得分:0)
你需要的是一种叫做类型擦除的东西。这是隐藏模板中类型的方法。
基本技术如下: - 在一个独立于类型的maner中声明一个具有所需行为的抽象类。 - 从该类派生模板类,实现其虚拟方法。
好消息,你可能不需要自己写boost::any
了。因为您需要的只是获取指针并将对象取回,这应该足够了。
现在,使用void*
是一个坏主意。如上所述,处理缓冲区的代码不应该关心类型。好的方法是与char*
合作。这是通常用于缓冲区的类型(例如socket apis)。它比更安全:标准中有一条特殊规则允许更安全地转换为char*
(参见别名规则)。
答案 2 :(得分:0)
这不是你问题的答案,但我只是想指出你的写作方式
Wrapper<T> Read() {
使它成为一个mutator成员函数,它按值返回,因此不是好习惯,因为它强制用户编写异常不安全代码。
出于同样的原因,STL stack::pop()
成员函数返回void,而不是从堆栈弹出的对象。