Reputation: 2917
I have a binary format that I'm writing encoders and decoders for. Almost all of the binary types directly map to primitives, except for two container types, a list and a map type that can contain any of the other types in the format including themselves.
These feel like they just want to be a typedef of std::variant
typedef std::variant<std::vector<char>, std::vector<int>, ...> ListType
But because I need to be able to contain a vector of ListType itself I end up doing this
struct ListType {
std::variant<std::vector<char>, std::vector<int>, ..., std::vector<ListType>> value;
}
Which adds a little friction to using the type. There's really no other state to these variables to justify encapsulating them.
Typing it out I realizing I'm asking "Can you forward declare a template?" which seems a stupid question. Still, anyone got a better strategy for this?
Upvotes: 3
Views: 1529
Reputation: 275330
template<class...Ts>
struct self_variant;
template<class...Ts>
using self_variant_base =
std::variant<
std::vector<Ts>...,
std::vector<self_variant<Ts...>>
>;
template<class...Ts>
struct self_variant:
self_variant_base<Ts...>
{
using self_variant_base<Ts...>::self_variant_base;
self_variant_base<Ts...> const& base() const { return *this; }
self_variant_base<Ts...>& base() { return *this; }
};
template<class T>
void print( T const& t ) {
std::cout << t << ",";
}
template<class T>
void print( std::vector<T> const& v ) {
std::cout << "[";
for (auto const& e:v) {
print(e);
}
std::cout << "]\n";
}
template<class...Ts>
void print( self_variant<Ts...> const& sv ) {
std::visit( [](auto& e){
print(e);
}, sv.base());
}
int main() {
self_variant<int, char> bob = std::vector<int>{1,2,3};
self_variant<int, char> alice = std::vector<self_variant<int, char>>{ bob, bob, bob };
print(alice);
}
so, the need for .base()
is because std::visit
was worded a bit wrong. I believe this will be fixed in a future standard revision.
In any case, this reduces the friction a bit.
Live example, 3 recursive depth live example.
Upvotes: 4