Consider the following C++ code.
#include <iostream>
#include <set>
#include <string>
enum class Field { kX, kY };
std::string ToString(const Field f) {
switch (f) {
case Field::kX:
return "x";
case Field::kY:
return "y";
default:
return "?";
}
}
std::set<std::string> FieldStrings(const bool has_x, const bool has_y) {
std::set<std::string> field_strings;
if (has_x) {
field_strings.insert(ToString(Field::kX));
}
if (has_y) {
field_strings.insert(ToString(Field::kY));
}
return field_strings;
}
template <Field... Args> struct S {
int x = 0; // Should be present if and only if `kX` in `Args`.
int y = 0; // Should be present if and only if `kY` in `Args`.
// Should return `ToString` called on all of the `Field`s in `Args`.
static const std::set<std::string> Fields() {
static const std::set<std::string> kFields;
return kFields;
}
};
template <bool HasX, bool HasY> struct T {
// Returns the fields that are available in the struct.
static const std::set<std::string> &Fields() {
static const std::set<std::string> kFields;
return kFields;
}
};
template <> struct T<false, true> {
int y = 0;
static const std::set<std::string> &Fields() {
static const std::set<std::string> kFields = FieldStrings(false, true);
return kFields;
}
};
template <> struct T<true, false> {
int x = 0;
static const std::set<std::string> &Fields() {
static const std::set<std::string> kFields = FieldStrings(true, false);
return kFields;
}
};
template <> struct T<true, true> {
int x = 0;
int y = 0;
static const std::set<std::string> &Fields() {
static const std::set<std::string> kFields = FieldStrings(true, true);
return kFields;
}
};
The struct S sketches out what I am hoping to achieve:
- a class template that takes a parameter pack of enums
- data members
S::xandS::ythat conditionally exist based on the contents of the template parameter pack - a static function member
S::Fieldsthat is obtained by transforming the contents of the template parameter pack
The struct T behaves more like what I am hoping to achieve:
- the data members
T::xandT::yconditionally exist based on the template parameters - the static function member
T::Fieldsreturns a value that depends on the template parameters
T does not behave exactly like what I want because there are separate bool parameters for the fields rather than a pack of Field enums. More importantly, the implementation of T is not scalable: the number of specializations increases exponentially in the number of fields. It is not too bad to write out all the overloads when there are two fields, but it becomes a huge burden if there are 10 fields.
Is there any way to implement S? The key requirements are:
- we pass in the fields that we want to support in the struct
- the data members exist conditionally based on the fields we pass in to the template pack
- the static function depends on the fields we pass in to the template pack
xoryanywhere. Why do you need them at all?