/static_any

generic stack-based container

Primary LanguageC++MIT LicenseMIT

Build Status Build status Coverage Status

static_any<S>

A container for generic (as general) data type — like boost.any. However:

  • It is ~10x faster than boost.any, mainly because there is no memory allocation
  • As it lies on the stack, it is cache-friendly, close to your other class attributes
  • There is a very small space overhead: a fixed overhead of 8 bytes

static_any<S> is also safe:

  • operations meet the strong exception guarantee
  • compile time check during the assignment, to ensure that its buffer is big enough to store the value
  • runtime check before any conversions, to ensure that the stored type is the one's requested by the user

Example

    static_any<32> a;
    static_assert(sizeof(a) == 32 + 8, "any has a fixed overhead of 8 bytes");

    a = 1234;
    ASSERT_EQ(1234, a.get<int>());

    a = std::string("foobar");
    ASSERT_EQ("foobar", a.get<std::string>());

    try {
      // This will throw as a std::string has been stored
      std::cout << any_cast<int>(a);
    }
    catch(bad_any_cast& ex) {
    }

    struct A : std::array<char, 32> {};
    a = A();

    struct B : A { char c; };  
    
    // Does not build: sizeof(B) is too big for static_any<16>
    a = B();

static_any_t<S>

A container similar to static_any<S>, but for trivially copyable types only. The differences:

  • No space overhead
  • Faster
  • Unsafe: there is no check when you try to access your data

Benchmarks

As the main advantage of static_any(_t<S>) is speed, here is a comparison of assign/get operations on a POD/non-POD types — an integer and a std::string — between boost.any, QVariant, static_any<S> and static_any_t<S>.

assign a double

Test             Time (ns)
--------------------------
qvariant                36
boost.any               57
std::any                 4
static_any<8>            4
static_any_t<8>          0

get a double

Test             Time (ns)
--------------------------
qvariant                 9
boost.any               12
std::any                 9
static_any<8>            9
static_any_t<8>          6

assign a small struct

Test             Time (ns)
--------------------------
qvariant               294
boost.any               47
std::any                54
static_any<8>           12
static_any_t<8>          0

get a small struct

Test             Time (ns)
--------------------------
qvariant                12
boost.any               48
std::any                 6 
static_any<8>            4
static_any_t<8>          0

assign a string

Test             Time (ns)
--------------------------
qvariant               400
boost.any              143
std::any               139
static_any<32>          92

get a string

Test             Time (ns)
--------------------------
qvariant                31
boost.any               64
std::any                30
static_any<32>           4

What I used for this benchmark:

  • Boost 1.54
  • Qt5
  • GCC 5.3.0
  • CPU i7-3537U

The code is available in benchmark.cpp. There is a dependency on geiger, a benchmarking library I developed. In order to build and run the benchmark, first install geiger:

git clone https://github.com/david-grs/geiger
cd geiger && mkdir build && cd build
cmake .. && make install

Then pass -DBUILD_BENCHMARK=1 to your cmake command.