Reputation: 6425
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?
E0,E1,E2,...
is not allowed, because of low maintainability.E0,E1,E2,...
to CCollection
manually.CCollection
should be "just work" for E1000.constexpr
if possible.E0,E1,E2,...
CCollection
's functions are never called for a certain E-X
,E-X
can be omitted (up to you).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.
Upvotes: 1
Views: 746
Reputation: 50540
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:
CCollection
class is designed so as it returns 0 from sizeOf
if you do that.That said, it can serve as a base for a more elaborated production code at least.
Upvotes: 1
Reputation: 217265
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;
};
Upvotes: 2