Index: llvm/trunk/include/llvm/ADT/Any.h =================================================================== --- llvm/trunk/include/llvm/ADT/Any.h +++ llvm/trunk/include/llvm/ADT/Any.h @@ -0,0 +1,147 @@ +//===- Any.h - Generic type erased holder of any type -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Any, a non-template class modeled in the spirit of +// std::any. The idea is to provide a type-safe replacement for C's void*. +// It can hold a value of any copy-constructible copy-assignable type +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ADT_ANY_H +#define LLVM_ADT_ANY_H + +#include "llvm/ADT/STLExtras.h" + +#include +#include +#include + +namespace llvm { + +class Any { + template struct TypeId { static const char Id = 0; }; + + struct StorageBase { + virtual ~StorageBase() = default; + virtual std::unique_ptr clone() const = 0; + virtual const void *id() const = 0; + }; + + template struct StorageImpl : public StorageBase { + explicit StorageImpl(const T &Value) : Value(Value) {} + + explicit StorageImpl(T &&Value) : Value(std::move(Value)) {} + + std::unique_ptr clone() const override { + return llvm::make_unique>(Value); + } + + const void *id() const override { return &TypeId::Id; } + + T Value; + + private: + StorageImpl &operator=(const StorageImpl &Other) = delete; + StorageImpl(const StorageImpl &Other) = delete; + }; + +public: + Any() = default; + + Any(const Any &Other) + : Storage(Other.Storage ? Other.Storage->clone() : nullptr) {} + + // When T is Any or T is not copy-constructible we need to explicitly disable + // the forwarding constructor so that the copy constructor gets selected + // instead. + template < + typename T, + typename std::enable_if< + llvm::conjunction< + llvm::negation::type, Any>>, + std::is_copy_constructible::type>>::value, + int>::type = 0> + Any(T &&Value) { + using U = typename std::decay::type; + Storage = llvm::make_unique>(std::forward(Value)); + } + + Any(Any &&Other) : Storage(std::move(Other.Storage)) {} + + Any &swap(Any &Other) { + std::swap(Storage, Other.Storage); + return *this; + } + + Any &operator=(Any Other) { + Storage = std::move(Other.Storage); + return *this; + } + + bool hasValue() const { return !!Storage; } + + void reset() { Storage.reset(); } + +private: + template friend T any_cast(const Any &Value); + template friend T any_cast(Any &Value); + template friend T any_cast(Any &&Value); + template friend const T *any_cast(const Any *Value); + template friend T *any_cast(Any *Value); + template friend bool any_isa(const Any &Value); + + std::unique_ptr Storage; +}; + +template bool any_isa(const Any &Value) { + if (!Value.Storage) + return false; + using U = + typename std::remove_cv::type>::type; + return Value.Storage->id() == &Any::TypeId::Id; +} + +template T any_cast(const Any &Value) { + using U = + typename std::remove_cv::type>::type; + return static_cast(*any_cast(&operand)); +} + +template T any_cast(Any &Value) { + using U = + typename std::remove_cv::type>::type; + return static_cast(*any_cast(&Value)); +} + +template T any_cast(Any &&Value) { + using U = + typename std::remove_cv::type>::type; + return static_cast(std::move(*any_cast(&Value))); +} + +template const T *any_cast(const Any *Value) { + using U = + typename std::remove_cv::type>::type; + assert(Value && any_isa(*Value) && "Bad any cast!"); + if (!Value || !any_isa(*Value)) + return nullptr; + return &static_cast &>(*Value->Storage).Value; +} + +template T *any_cast(Any *Value) { + using U = typename std::decay::type; + assert(Value && any_isa(*Value) && "Bad any cast!"); + if (!Value || !any_isa(*Value)) + return nullptr; + return &static_cast &>(*Value->Storage).Value; +} + +} // end namespace llvm + +#endif // LLVM_ADT_ANY_H Index: llvm/trunk/include/llvm/ADT/STLExtras.h =================================================================== --- llvm/trunk/include/llvm/ADT/STLExtras.h +++ llvm/trunk/include/llvm/ADT/STLExtras.h @@ -58,6 +58,18 @@ } // end namespace detail //===----------------------------------------------------------------------===// +// Extra additions to +//===----------------------------------------------------------------------===// + +template struct negation : std::bool_constant {}; + +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction + : std::conditional, B1>::type {}; + +//===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// Index: llvm/trunk/unittests/ADT/AnyTest.cpp =================================================================== --- llvm/trunk/unittests/ADT/AnyTest.cpp +++ llvm/trunk/unittests/ADT/AnyTest.cpp @@ -0,0 +1,173 @@ +//===- llvm/unittest/Support/AnyTest.cpp - Any tests ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Any.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; + +namespace { + +// Make sure we can construct, copy-construct, move-construct, and assign Any's. +TEST(AnyTest, ConstructionAndAssignment) { + llvm::Any A; + llvm::Any B{7}; + llvm::Any C{8}; + llvm::Any D{"hello"}; + llvm::Any E{3.7}; + + // An empty Any is not anything. + EXPECT_FALSE(A.hasValue()); + EXPECT_FALSE(any_isa(A)); + + // An int is an int but not something else. + EXPECT_TRUE(B.hasValue()); + EXPECT_TRUE(any_isa(B)); + EXPECT_FALSE(any_isa(B)); + + EXPECT_TRUE(C.hasValue()); + EXPECT_TRUE(any_isa(C)); + + // A const char * is a const char * but not an int. + EXPECT_TRUE(D.hasValue()); + EXPECT_TRUE(any_isa(D)); + EXPECT_FALSE(any_isa(D)); + + // A double is a double but not a float. + EXPECT_TRUE(E.hasValue()); + EXPECT_TRUE(any_isa(E)); + EXPECT_FALSE(any_isa(E)); + + // After copy constructing from an int, the new item and old item are both + // ints. + llvm::Any F(B); + EXPECT_TRUE(B.hasValue()); + EXPECT_TRUE(F.hasValue()); + EXPECT_TRUE(any_isa(F)); + EXPECT_TRUE(any_isa(B)); + + // After move constructing from an int, the new item is an int and the old one + // isn't. + llvm::Any G(std::move(C)); + EXPECT_FALSE(C.hasValue()); + EXPECT_TRUE(G.hasValue()); + EXPECT_TRUE(any_isa(G)); + EXPECT_FALSE(any_isa(C)); + + // After copy-assigning from an int, the new item and old item are both ints. + A = F; + EXPECT_TRUE(A.hasValue()); + EXPECT_TRUE(F.hasValue()); + EXPECT_TRUE(any_isa(A)); + EXPECT_TRUE(any_isa(F)); + + // After move-assigning from an int, the new item and old item are both ints. + B = std::move(G); + EXPECT_TRUE(B.hasValue()); + EXPECT_FALSE(G.hasValue()); + EXPECT_TRUE(any_isa(B)); + EXPECT_FALSE(any_isa(G)); +} + +TEST(AnyTest, GoodAnyCast) { + llvm::Any A; + llvm::Any B{7}; + llvm::Any C{8}; + llvm::Any D{"hello"}; + llvm::Any E{'x'}; + + // Check each value twice to make sure it isn't damaged by the cast. + EXPECT_EQ(7, llvm::any_cast(B)); + EXPECT_EQ(7, llvm::any_cast(B)); + + EXPECT_STREQ("hello", llvm::any_cast(D)); + EXPECT_STREQ("hello", llvm::any_cast(D)); + + EXPECT_EQ('x', llvm::any_cast(E)); + EXPECT_EQ('x', llvm::any_cast(E)); + + llvm::Any F(B); + EXPECT_EQ(7, llvm::any_cast(F)); + EXPECT_EQ(7, llvm::any_cast(F)); + + llvm::Any G(std::move(C)); + EXPECT_EQ(8, llvm::any_cast(G)); + EXPECT_EQ(8, llvm::any_cast(G)); + + A = F; + EXPECT_EQ(7, llvm::any_cast(A)); + EXPECT_EQ(7, llvm::any_cast(A)); + + E = std::move(G); + EXPECT_EQ(8, llvm::any_cast(E)); + EXPECT_EQ(8, llvm::any_cast(E)); + + // Make sure we can any_cast from an rvalue and that it's properly destroyed + // in the process. + EXPECT_EQ(8, llvm::any_cast(std::move(E))); + EXPECT_TRUE(E.hasValue()); + + // Make sure moving from pointers gives back pointers, and that we can modify + // the underlying value through those pointers. + EXPECT_EQ(7, *llvm::any_cast(&A)); + int *N = llvm::any_cast(&A); + *N = 42; + EXPECT_EQ(42, llvm::any_cast(A)); + + // Make sure that we can any_cast to a reference and this is considered a good + // cast, resulting in an lvalue which can be modified. + llvm::any_cast(A) = 43; + EXPECT_EQ(43, llvm::any_cast(A)); +} + +TEST(AnyTest, CopiesAndMoves) { + struct TestType { + TestType() = default; + TestType(const TestType &Other) + : Copies(Other.Copies + 1), Moves(Other.Moves) {} + TestType(TestType &&Other) : Copies(Other.Copies), Moves(Other.Moves + 1) {} + int Copies = 0; + int Moves = 0; + }; + + // One move to get TestType into the Any, and one move on the cast. + TestType T1 = llvm::any_cast(Any{TestType()}); + EXPECT_EQ(0, T1.Copies); + EXPECT_EQ(2, T1.Moves); + + // One move to get TestType into the Any, and one copy on the cast. + Any A{TestType()}; + TestType T2 = llvm::any_cast(A); + EXPECT_EQ(1, T2.Copies); + EXPECT_EQ(1, T2.Moves); + + // One move to get TestType into the Any, and one move on the cast. + TestType T3 = llvm::any_cast(std::move(A)); + EXPECT_EQ(0, T3.Copies); + EXPECT_EQ(2, T3.Moves); +} + +TEST(AnyTest, BadAnyCast) { + llvm::Any A; + llvm::Any B{7}; + llvm::Any C{"hello"}; + llvm::Any D{'x'}; + + EXPECT_DEBUG_DEATH(llvm::any_cast(A), ""); + + EXPECT_DEBUG_DEATH(llvm::any_cast(B), ""); + EXPECT_DEBUG_DEATH(llvm::any_cast(B), ""); + + EXPECT_DEBUG_DEATH(llvm::any_cast(C), ""); + + EXPECT_DEBUG_DEATH(llvm::any_cast(D), ""); +} + +} // anonymous namespace Index: llvm/trunk/unittests/ADT/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/ADT/CMakeLists.txt +++ llvm/trunk/unittests/ADT/CMakeLists.txt @@ -3,6 +3,7 @@ ) add_llvm_unittest(ADTTests + AnyTest.cpp APFloatTest.cpp APIntTest.cpp APSIntTest.cpp