问题
There are many type of elements E0,E1,...
, all of them derived from Element
.
class Element{ public: int id=0; } //"Element" can be edited
class E0: public Element{ ... }
class E1: public Element{ ... }
...
class E999: public Element{ ... }
How to cache sizeof()
for E0,E1,...
?
It has to return the correct value [#2] later by "custom typeid" [#1].
class CCollection{
template<class EX> EX* create(){ //(Edit)
//all EX* is created by this function
}
/** user only pass EX*, never pass Element* */ (Edit)
int toId(Element* element){//[#1] should replaced by template?
//....... return id, e.g. 0 for E0, 1 for E2, 2 for E1, ...
// can be in any order, but must unique and packed ........
}
int sizeOf(int id){//[#2]
//....... return size of EX
// must consistent with toId() ,
// e.g. sizeof(E0) for 0, sizeof(E2) for 1, sizeof(E1) for 2 ........
}
//....... other fields / functions, if necessary ............
}
How to implement this?
Requirement:
- Manually assign id-integer of
E0,E1,E2,...
is not allowed, because of low maintainability. - User doesn't have to "register"
E0,E1,E2,...
toCCollection
manually.
When new type of Element, e.g. E1000, no modification have to be done.
..... i.e.CCollection
should be "just work" for E1000. - Highest performance, e.g. :-
- Virtual calling should be avoided, except totally unavoidable.
- Use
constexpr
if possible. - Static variable inside a function is not good.
- Don't modify code of
E0,E1,E2,...
Flexibility in design
- If
CCollection
's functions are never called for a certainE-X
,
the management forE-X
can be omitted (up to you). - There is only 1 CCollection instance.
clue1: Template seems to be the most promising path, but I am not sure.
clue2: (Edit) This snippet I have planed may help :-
template<class EX> int toId(EX* element){//[#1]
// register something?
}
As you can guess, the real problem is far bigger than this,
but this is the only one missing jigsaw of my whole puzzle.
Although I wish for code, just a guide is really appreciated.
回答1:
If you can add an intermediate class between Element
and each EX
, here is a possible solution (with a minimal, working example):
#include<cstddef>
#include<memory>
#include<vector>
#include<cassert>
struct Element {
static std::size_t counter;
};
std::size_t Element::counter = 0;
template<typename>
struct ElementType: Element {
static const int type;
};
template<typename T>
const int ElementType<T>::type = Element::counter++;
struct E0: ElementType<E0> {};
struct E1: ElementType<E1> { int i; };
struct E2: ElementType<E2> {};
class CCollection {
void ensure(std::size_t type) {
if(!(type < sizes.size())) {
sizes.resize(type+1, 0);
}
}
template<typename EX>
void ensure() {
ensure(EX::type);
sizes[EX::type] = sizeof(EX);
}
public:
template<class EX>
std::shared_ptr<EX> create() {
ensure<EX>();
return std::make_shared<EX>();
}
template<typename EX>
std::size_t toId() {
ensure<EX>();
return EX::type;
}
std::size_t sizeOf(std::size_t type) {
ensure(type);
return sizes[type];
}
private:
std::vector<std::size_t> sizes;
};
int main() {
CCollection coll;
assert(coll.toId<E0>() != coll.toId<E1>());
assert(coll.toId<E0>() != coll.toId<E2>());
assert(coll.toId<E1>() != coll.toId<E2>());
assert(coll.sizeOf(0) == sizeof(E0));
assert(coll.sizeOf(1) == sizeof(E1));
assert(coll.sizeOf(2) == sizeof(E2));
assert(coll.sizeOf(0) != coll.sizeOf(1));
// ...
}
The problem in the code in the example arises if one tries to create instances of EX
without using create
.
Anyway:
- You said it should never happen.
- You can enforce the use of that function.
- The
CCollection
class is designed so as it returns 0 fromsizeOf
if you do that.
That said, it can serve as a base for a more elaborated production code at least.
回答2:
I think following doesn't break any of your rules:
struct CCollection
{
template <typename T>
std::size_t toId() {
auto it = info.find(typeid(T));
if (it == info.end()) {
it = info.insert({typeid(T), {count++, sizeof(T)}}).first;
}
return it->second.id;
}
std::size_t sizeOf(const Base& base) const {
const auto& data = info.at(typeid(base));
return data.size;
}
std::size_t count = 0;
std::unordered_map<std::type_index, Data> info;
};
Demo
来源:https://stackoverflow.com/questions/39075804/create-custom-typeid-for-many-types-and-cache-their-sizeof