A Better void*: std::any

Default Initialization

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, default_ctor)
{
    std::any a;

    ASSERT_EQ(a.type(), typeid(void));
    ASSERT_FALSE(a.has_value());
}

Initialization, Assignment, Access

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, basic)
{
    std::any a = 42;                                   // <--- int
    ASSERT_EQ(a.type(), typeid(int));
    int i = std::any_cast<int>(a);
    ASSERT_EQ(i, 42);

    a = 37.6;                                          // <--- double
    ASSERT_EQ(a.type(), typeid(double));
    double d = std::any_cast<double>(a);
    ASSERT_FLOAT_EQ(d, 37.6);
}

Copy, And Resource Management

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, copy)
{
    std::any a1 = std::string("howdy");

    std::any a2 = a1;                                  // <--- copy ctor
    ASSERT_EQ(std::any_cast<std::string>(a2), "howdy");

    std::any a3 = std::vector<int>{1, 2, 3, 4, 5, 6};

    a3 = a2;                                           // <--- resource management!

    ASSERT_EQ(std::any_cast<std::string>(a3), "howdy");
}

Run-Time Type Errors

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, bad_cast)
{
    std::any a = 42;
    ASSERT_THROW(
        {
            std::any_cast<std::string>(a);
        },
        std::bad_any_cast
    );
}

has_value(): Null?

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, has_value)
{
    std::any a = 42;
    ASSERT_TRUE(a.has_value());

    a.reset();
    ASSERT_FALSE(a.has_value());
}

Avoiding Exceptions

  • any_cast<> on a pointer is different

  • does not throw

  • ⟶ suitable for if

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, pointer_cast)
{
    std::any a = 42;

    ASSERT_NE(std::any_cast<int>(&a), nullptr);
    ASSERT_EQ(std::any_cast<const double>(&a), nullptr);
}

Modifying In-Place: std::any_cast<> To Reference

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, reference_cast)
{
    std::any a = 42;
    int& i = std::any_cast<int&>(a);                   // <--- pointer to any-content

    i = 666;                                           // <--- modify a
    ASSERT_EQ(std::any_cast<int>(a), 666);             // <--- a has been modified
}

std::string And const char* Are Different

#include <gtest/gtest.h>
#include <any>

TEST(any_suite, char_ptr_careful)
{
    std::any a = "howdy";                              // <-- careful: const char*, not std::string!
    ASSERT_EQ(a.type(), typeid(const char*));

    const char* s = std::any_cast<const char*>(a);
    ASSERT_EQ(s, "howdy");                             // <--- this is not obvious!
    char* cmp = new char[]{"howdy"};
    ASSERT_NE(s, cmp);
    delete[] cmp;
}