You can do that with a bit of trickery but I am not sure how useful it really is.
The main problem you will have to face it that generally you can't iterate easily through all values of an enum. As the underlying data type for a normal enum is integer starting you might use a simple loop for a plain enum but this fails for any enum where values are set manually such as
enum class AudioEffect {
Chorus = 3,
Filter = 6,
Gain = 5,
Oscillator = 2,
Phaser = 1
};
Generally you will have to create an additional data structure like a std::tuple or a std::vector<AudioEffect>, add all the possible values and loop over using something like a variadic template (As soon as you use template parameters a normal loop won't suffice anymore). Furthermore in C++ there is no such thing as virtual static functions.
Specialised template class
The best I can come up with is:
Declare a class template <AudioEffect E> class Processor and specialise it for all possible template parameters. I added two static constexpr functions for getting the min and the max. These will have to be declared in all specialisations. I have used two functions min and max that return a minimum and a maximum value but you could return a std::string_view (or in C++20 you will be able to return a constexpr std::string) that contains more information about the different limits.
template <AudioEffect E>
class Processor: ProcessorBase {
};
template <>
class Processor<AudioEffect::Chorus>: ProcessorBase {
public:
// Or a single function for printing all the settings of this class
static constexpr int min() {
return 1;
}
static constexpr int max() {
return 99;
}
};
Then I can print all the limits with a variadic template function and C++17 fold expressions (assuming here that each instance of the template class Preprocessor has a static constexpr function min and another one max
template <AudioEffect... E>
std::string print() {
std::stringstream ss;
((ss << E << "\t min: " << Processor<E>::min() << ",\t max: " << Processor<E>::max() << std::endl), ...);
return ss.str();
}
This print function can be called inside the main listing all possible parameters
std::cout << print<AudioEffect::Chorus,AudioEffect::Filter,AudioEffect::Gain,AudioEffect::Oscillator,AudioEffect::Phaser>() << std::endl;
Try it here!
Map
Another similar way would be to map the enum to the classes (that do not have to be specialised templates anymore) with traits like here by
Defining your classes like
class ChorusProcessor: ProcessorBase {
public:
static constexpr int min() {
return 1;
}
static constexpr int max() {
return 99;
}
};
Then specialising a template struct Map that maps in between the enum values and the classes
template<AudioEffect> struct Map;
template<> struct Map<AudioEffect::Chorus> {
using type = ChorusProcessor;
};
And introducting a print function again with variadic templates like
template <AudioEffect... E>
std::string print() {
std::stringstream ss;
((ss << E << "\t min: " << Map<E>::type::min() << ",\t max: " << Map<E>::type::max() << std::endl), ...);
return ss.str();
}
Try it here!
This is pretty similar to the solution proposed in the comments that makes use of an std::tuple
using Processors = std::tuple<
ChorusProcessor,
FilterProcessor,
GainProcessor,
OscilatorProcessor,
PhaserProcessor
>;
std::tuple_element_t<AudioEffect::Filter, Processors>::value
Brief but dirty
If you use an enum without specifying values explicitly you could also go away with a dirty solution only supplying the first and the last value of the enum (C++17)
template <AudioEffect Begin, AudioEffect End>
std::string print() {
std::stringstream ss;
ss << Begin << "\t min: " << Processor<Begin>::min() << ",\t max: " << Processor<Begin>::max() << std::endl;
if constexpr (Begin != End) {
ss << print<static_cast<AudioEffect>(static_cast<int>(Begin)+1), End>();
}
return ss.str();
}
This way you could simply call it with the first and the last value of the enum
std::cout << print<AudioEffect::Chorus,AudioEffect::Phaser>() << std::endl;
Try it here!
Enum reflection
Finally with the GCC, Clang and MSVC compiler there is a hacky way to get some enum properties with the __PRETTY_FUNCTION__ identifier which is described here. (There are a few typos in the original code and it is not constexpr so better try it here.)
With it you could completely get rid of the Begin and End by looping from 0 to the detected number of enum values. This is a bit more complicated so I will just leave the code here for you to try. With it you can output all classes without specifying all the enum values explicitly:
std::cout << print<AudioEffect>() << std::endl;