I'm trying to create a struct of arrays:
auto x = MakeMegaContainer<StructA, StructB, StructC>();
Which I want to, at compile time, produce a structure like:
struct MegaContainer {
std::tuple< Container<StructA>, Container<StructB>, Container<StructC> > Data;
};
The creation method is non-negotiable, and I think tuples are the best way to go, as later I could access one container's struct elements with std::get<StructA>(x)[index]
, if my container allows []
.
I tried doing something like:
template<typename... LilStructs>
class StructStorage {
public:
template<typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Get(id); // Get is defined for MyContainer
}
template<typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Contains(id); // Contains is defined for MyContainer
}
private:
std::tuple<MyContainer<LilStructs>...> m_Storage;
};
But I don't think this works. I'm not sure how to take the variadic arguments and "wrap" them with a container
What should I do instead?
Follow up question: The MyContainer
also takes additional arguments that customise it, like a max size. Is there a nice way of passing that, something like template<typename... LilStructs, uint64_t MAX_SIZE=4096>
?
Edit: My question was more theoretical, but since it's asked, I'll provide a stripped down version of the container for a minimum reproducible example:
template<typename T_elem, typename T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
MyContainer() = default;
~MyContainer() = default;
bool Contains(const T_int& id) const;
T_elem& operator[](const T_int& id);
T_elem& Get(const T_int& id);
private:
std::map<T_int, T_elem> m_Map;
T_int m_Size = 0;
};
template<typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(
const T_int& id
) const {
return m_Map[id] < m_Size;
}
template<typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
return m_Map[id];
}
template<typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
return operator[](id);
}
When I try to compile:
void Test() {
struct SA { int x; };
struct SB { float x; };
struct SC { char x; };
auto& tests = StructStorage <SA, SB, SC>();
bool x = !tests.ContainsStruct<SA>(5);
}
I get the error:
c:\...\test.h(18): error C2039: 'Contains': is not a member of 'Trial::SA'
As I said, I know the error is in the std::tuple<MyContainer<LilStructs>...> m_Storage;
line, and the error is indpendent of the implementation of MyContainer
(provided that Contains
and Get
are implemented), but I do not know what to replace it with, or how to achieve the functionality I'm looking for (which was already described).
CodePudding user response:
Look at this part:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Get(id); // Get is defined for MyContainer
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<OneStruct>(m_Storage).Contains(id); // Contains is defined for MyContainer
}
The m_Storage
tuple is a tuple of MyContainer<LilStructs>...
s and not LilStructs...
s so what you actually want to do is this:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Get(id); // Get is defined for MyContainer
// ^^^^^^^^^^^^^^^^^^^^^^
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id); // Contains is defined for MyContainer
// ^^^^^^^^^^^^^^^^^^^^^^
}
Also, your Contains()
function is wrong. Use this:
template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
return m_Map.find(id) != m_Map.end();
}
Full working code:
#include <cstdint>
#include <cassert>
#include <tuple>
#include <map>
template <typename T_elem, typename T_int = uint64_t, T_int MAX_SIZE = 4096>
class MyContainer{
public:
MyContainer() = default;
~MyContainer() = default;
bool Contains(const T_int& id) const;
T_elem& operator[](const T_int& id);
T_elem& Get(const T_int& id);
private:
std::map<T_int, T_elem> m_Map;
T_int m_Size = 0;
};
template <typename T_elem, typename T_int, T_int MAX_SIZE>
bool MyContainer<T_elem, T_int, MAX_SIZE>::Contains(const T_int& id) const {
return m_Map.find(id) != m_Map.end();
}
template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::operator[](const T_int& id) {
return m_Map[id];
}
template <typename T_elem, typename T_int, T_int MAX_SIZE>
T_elem& MyContainer<T_elem, T_int, MAX_SIZE>::Get(const T_int& id) {
return operator[](id);
}
template <typename ...LilStructs>
class StructStorage {
public:
template <typename OneStruct>
OneStruct& GetStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Get(id);
}
template <typename OneStruct>
bool ContainsStruct(const uint64_t& id) {
return std::get<MyContainer<OneStruct>>(m_Storage).Contains(id);
}
private:
std::tuple<MyContainer<LilStructs>...> m_Storage;
};
int main() {
struct SA { int x; };
struct SB { float x; };
struct SC { char x; };
auto tests = StructStorage<SA, SB, SC>();
assert(!tests.ContainsStruct<SA>(5));
}