Skip to content

yuqian90/integral_switch

Repository files navigation

integral_switch

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.

travis godbolt License: GPL v3

Introduction

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

Examples

Example 1

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>

Example 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

About

integral_switch can be used to choose a static integral_constant type based on a runtime integral value.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published