integral_switch maps a runtime integral value to a compile-time static type. The performance is comparable to that of a hand-rolled switch-case statement.
integral_switch makes it possible to write the following construct in a reusable manner without sacrificing performance. Such switch-case constructs are often written to map a runtime integral value to a static type in scenarios such as factory function, state machine or deserialization. switch-case statements are efficient but they are hard to reuse. There is no easy way around them without resorting to techniques such as x-macros.
switch(msg.msg_id())
{
case MsgId::type0:
return MsgType0::deserialize(msg);
case MsgId::type1:
return MsgType1::deserialize(msg);
case MsgId::type2:
return MsgType2::deserialize(msg);
...
}
integral_switch offers an alternative that is equally fast and much more reusable. The underlying implementation uses recursive compile-time switch-case generation that is easy for compilers to optimize, making it as fast as hand-rolled switch-case statement.
- Works for arbitrarily large number of elements
- No code generation
- No macro magic
- No extra dependencies
- No heap allocation
- As fast as switch-case statements. See benchmark
- Continuously tested against several versions of clang/gcc/xcode for C++11/14/17
- Single-header
integral_switch::visit() maps integeral values to the corresponding std::integral_constant type.
#include <iostream>
#include "integral_switch.h"
using IntegralSwitch = integral_switch::integral_switch<int, 0, 1, 2>;
struct Visitor {
void operator()(std::integral_constant<int, 0>) const {
std::cout << "0: std::integral_constant<int, 0>" << '\n';
}
void operator()(std::integral_constant<int, 1>) const {
std::cout << "1: std::integral_constant<int, 1>" << '\n';
}
void operator()(std::integral_constant<int, 2>) const {
std::cout << "2: std::integral_constant<int, 2>" << '\n';
}
};
int main()
{
Visitor visitor;
for (int i = 0; i <= 2; ++i) {
IntegralSwitch::visit(visitor, i);
}
}
Output:
0: std::integral_constant<int, 0>
1: std::integral_constant<int, 1>
2: std::integral_constant<int, 2>
variadic_switch::visit() offers a slightly different interface that maps a runtime index value to the type corresponding to that index in a variadic type list.
#include <iostream>
#include "integral_switch.h"
struct Type0 {
void print() const { std::cout << "Type0" << '\n'; }
};
struct Type1 {
void print() const { std::cout << "Type1" << '\n'; }
};
struct Type2 {
void print() const { std::cout << "Type2" << '\n'; }
};
using VariadicSwitch = variadic_switch<Type0, Type1, Type2>;
struct Printer {
template <typename T> void operator()(type<T>) const { T{}.print(); }
};
int main()
{
Printer visitor;
for (int i = 0; i <= 2; ++i) {
VariadicSwitch::visit(visitor, i);
}
}
Output:
Type0
Type1
Type2