diff --git a/src/util/any.hpp b/src/util/any.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..50b29ec78eda6afbd853cf356d4a235b3ae0ffd9
--- /dev/null
+++ b/src/util/any.hpp
@@ -0,0 +1,234 @@
+#pragma once
+
+#include <memory>
+#include <typeinfo>
+#include <type_traits>
+
+#include <util/meta.hpp>
+
+// Partial implementation of std::any from C++17 standard.
+//      http://en.cppreference.com/w/cpp/utility/any
+//
+// Implements a standard-compliant subset of the full interface.
+//
+// - Does not avoid dynamic allocation of small objects.
+// - Does not implement the in_place_type<T> constructors from the standard.
+// - Does not implement the emplace modifier from the standard.
+
+namespace nest {
+namespace mc {
+namespace util {
+
+// Defines a type of object to be thrown by the value-returning forms of
+// util::any_cast on failure.
+//      http://en.cppreference.com/w/cpp/utility/any/bad_any_cast
+class bad_any_cast: public std::bad_cast {
+public:
+    const char* what() const noexcept override {
+        return "bad any cast";
+    }
+};
+
+class any {
+public:
+    constexpr any() = default;
+
+    any(const any& other): state_(other.state_->copy()) {}
+
+    any(any&& other) noexcept {
+        std::swap(other.state_, state_);
+    }
+
+    template <
+        typename T,
+        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, any>::value>
+    >
+    any(T&& other) {
+        using contained_type = util::decay_t<T>;
+        static_assert(std::is_copy_constructible<contained_type>::value,
+            "Type of contained object stored in any must satisfy the CopyConstructible requirements.");
+
+        state_.reset(new model<contained_type>(std::forward<T>(other)));
+    }
+
+    any& operator=(const any& other) {
+        state_.reset(other.state_->copy());
+        return *this;
+    }
+
+    any& operator=(any&& other) noexcept {
+        swap(other);
+        return *this;
+    }
+
+    template <
+        typename T,
+        typename = typename util::enable_if_t<!std::is_same<util::decay_t<T>, any>::value>
+    >
+    any& operator=(T&& other) {
+        using contained_type = util::decay_t<T>;
+
+        static_assert(std::is_copy_constructible<contained_type>::value,
+            "Type of contained object stored in any must satisfy the CopyConstructible requirements.");
+
+        state_.reset(new model<contained_type>(std::forward<T>(other)));
+        return *this;
+    }
+
+    void reset() noexcept {
+        state_.reset(nullptr);
+    }
+
+    void swap(any& other) noexcept {
+        std::swap(other.state_, state_);
+    }
+
+    bool has_value() const noexcept {
+        return (bool)state_;
+    }
+
+    const std::type_info& type() const noexcept {
+        return has_value()? state_->type(): typeid(void);
+    }
+
+private:
+    struct interface {
+        virtual ~interface() = default;
+        virtual const std::type_info& type() = 0;
+        virtual interface* copy() = 0;
+        virtual void* pointer() = 0;
+        virtual const void* pointer() const = 0;
+    };
+
+    template <typename T>
+    struct model: public interface {
+        ~model() = default;
+
+        model(const T& other): value(other) {}
+
+        model(T&& other): value(std::move(other)) {}
+
+        interface* copy() override {
+            return new model<T>(*this);
+        }
+
+        const std::type_info& type() override {
+            return typeid(T);
+        }
+
+        void* pointer() override {
+            return &value;
+        }
+
+        const void* pointer() const override {
+            return &value;
+        }
+
+        T value;
+    };
+
+    std::unique_ptr<interface> state_;
+
+protected:
+
+    template <typename T>
+    friend const T* any_cast(const any* operand);
+
+    template <typename T>
+    friend T* any_cast(any* operand);
+
+    template <typename T>
+    T* unsafe_cast() {
+        return static_cast<T*>(state_->pointer());
+    }
+
+    template <typename T>
+    const T* unsafe_cast() const {
+        return static_cast<const T*>(state_->pointer());
+    }
+};
+
+namespace impl {
+
+template <typename T>
+using any_cast_remove_qual = typename
+    std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+} // namespace impl
+
+// If operand is not a null pointer, and the typeid of the requested T matches
+// that of the contents of operand, a pointer to the value contained by operand,
+// otherwise a null pointer.
+template<class T>
+const T* any_cast(const any* operand) {
+    if (operand && operand->type()==typeid(T)) {
+        return operand->unsafe_cast<T>();
+    }
+    return nullptr;
+}
+
+template<class T>
+T* any_cast(any* operand) {
+    if (operand && operand->type()==typeid(T)) {
+        return operand->unsafe_cast<T>();
+    }
+    return nullptr;
+}
+
+template<class T>
+T any_cast(const any& operand) {
+    using U = impl::any_cast_remove_qual<T>;
+    static_assert(std::is_constructible<T, const U&>::value,
+        "any_cast type can't construct copy of contained object");
+
+    auto ptr = any_cast<U>(&operand);
+    if (ptr==nullptr) {
+        throw bad_any_cast();
+    }
+    return static_cast<T>(*ptr);
+}
+
+template<class T>
+T any_cast(any& operand) {
+    using U = impl::any_cast_remove_qual<T>;
+    static_assert(std::is_constructible<T, U&>::value,
+        "any_cast type can't construct copy of contained object");
+
+    auto ptr = any_cast<U>(&operand);
+    if (ptr==nullptr) {
+        throw bad_any_cast();
+    }
+    return static_cast<T>(*ptr);
+}
+
+template<class T>
+T any_cast(any&& operand) {
+    using U = impl::any_cast_remove_qual<T>;
+    static_assert(std::is_constructible<T, U>::value,
+        "any_cast type can't construct copy of contained object");
+
+    auto ptr = any_cast<U>(&operand);
+    if (ptr==nullptr) {
+        throw bad_any_cast();
+    }
+    return static_cast<T>(std::move(*ptr));
+}
+
+// Constructs an any object containing an object of type T, passing the
+// provided arguments to T's constructor.
+//
+// This does not exactly follow the standard, which states that
+// make_any is equivalent to
+//   return std::any(std::in_place_type<T>, std::forward<Args>(args)...);
+// i.e. that the contained object should be constructed in place, whereas
+// this implementation constructs the object, then moves it into the
+// contained object.
+// FIXME: rewrite with in_place_type when available.
+template <class T, class... Args>
+any make_any(Args&&... args) {
+    return any(T(std::forward<Args>(args) ...));
+}
+
+} // namespace util
+} // namespace mc
+} // namespace nest
diff --git a/tests/global_communication/test_domain_decomposition.cpp b/tests/global_communication/test_domain_decomposition.cpp
index ad0048f0cffc529faa49d2d26cc60f66da43b36b..bd6eb35c3d84fe9694a794e6c8093af91436ee73 100644
--- a/tests/global_communication/test_domain_decomposition.cpp
+++ b/tests/global_communication/test_domain_decomposition.cpp
@@ -13,15 +13,15 @@ using namespace nest::mc;
 
 using communicator_type = communication::communicator<communication::global_policy>;
 
-static bool is_dry_run() {
+inline bool is_dry_run() {
     return communication::global_policy::kind() ==
         communication::global_policy_kind::dryrun;
 }
 
 TEST(domain_decomp, basic) {
+/*
     using policy = communication::global_policy;
 
-/*
     const auto num_domains = policy::size();
     const auto rank = policy::id();
 */
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index ecae57a183301a035d4d0b4d117d2594b0eaff31..6a5c151ad2d06519192d637d379e2c1f009bd78f 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -31,6 +31,7 @@ set(TEST_CUDA_SOURCES
 set(TEST_SOURCES
     # unit tests
     test_algorithms.cpp
+    test_any.cpp
     test_backend.cpp
     test_double_buffer.cpp
     test_cell.cpp
diff --git a/tests/unit/test_any.cpp b/tests/unit/test_any.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2564bf4ce4f892a71dae1146f29ba85b67fb71c8
--- /dev/null
+++ b/tests/unit/test_any.cpp
@@ -0,0 +1,309 @@
+#include "../gtest.h"
+#include "common.hpp"
+
+#include <iostream>
+
+#include <util/any.hpp>
+
+using namespace nest::mc;
+
+TEST(any, copy_construction) {
+    util::any any_int(2);
+    EXPECT_EQ(any_int.type(), typeid(int));
+
+    util::any any_float(2.0f);
+    EXPECT_EQ(any_float.type(), typeid(float));
+
+    std::string str = "hello";
+    util::any any_string(str);
+    EXPECT_EQ(any_string.type(), typeid(std::string));
+}
+
+namespace {
+
+    struct moveable {
+        moveable() = default;
+
+        moveable(moveable&& other):
+            moves(other.moves+1), copies(other.copies)
+        {}
+
+        moveable(const moveable& other):
+            moves(other.moves), copies(other.copies+1)
+        {}
+
+        int moves=0;
+        int copies=0;
+    };
+
+}
+
+TEST(any, move_construction) {
+    moveable m;
+
+    util::any copied(m);
+    util::any moved(std::move(m));
+
+    // Check that the expected number of copies and moves were performed.
+    // Note that any_cast(any*) is used instead of any_cast(const any&) because
+    // any_cast(const any&) returns a copy.
+    const auto& cref = *util::any_cast<moveable>(&copied);
+    EXPECT_EQ(cref.moves, 0);
+    EXPECT_EQ(cref.copies, 1);
+
+    const auto& mref = *util::any_cast<moveable>(&moved);
+    EXPECT_EQ(mref.moves, 1);
+    EXPECT_EQ(mref.copies, 0);
+
+    // construction by any&& should not make any copies or moves of the
+    // constructed value
+    util::any fin(std::move(moved));
+    EXPECT_FALSE(moved.has_value()); // moved has been moved from and should be empty
+    const auto& fref = *util::any_cast<moveable>(&fin);
+    EXPECT_EQ(fref.moves, 1);
+    EXPECT_EQ(fref.copies, 0);
+
+    const auto value = util::any_cast<moveable>(fin);
+    EXPECT_EQ(value.moves, 1);
+    EXPECT_EQ(value.copies, 1);
+}
+
+TEST(any, type) {
+    using util::any;
+
+    any anyi(42);
+    any anys(std::string("hello"));
+    any anyv(std::vector<int>{1, 2, 3});
+    any any0;
+
+    EXPECT_EQ(typeid(int), anyi.type());
+    EXPECT_EQ(typeid(std::string), anys.type());
+    EXPECT_EQ(typeid(std::vector<int>), anyv.type());
+    EXPECT_EQ(typeid(void), any0.type());
+
+    anyi.reset();
+    EXPECT_EQ(typeid(void), anyi.type());
+
+    anyi = std::true_type();
+    EXPECT_EQ(typeid(std::true_type), anyi.type());
+}
+
+TEST(any, swap) {
+    using util::any;
+    using util::any_cast;
+
+    any any1(42);     // integer
+    any any2(3.14);   // double
+
+    EXPECT_EQ(typeid(int),    any1.type());
+    EXPECT_EQ(typeid(double), any2.type());
+
+    any1.swap(any2);
+
+    EXPECT_EQ(any_cast<int>(any2), 42);
+    EXPECT_EQ(any_cast<double>(any1), 3.14);
+
+    EXPECT_EQ(typeid(double), any1.type());
+    EXPECT_EQ(typeid(int),    any2.type());
+
+    any1.swap(any2);
+
+    EXPECT_EQ(any_cast<double>(any2), 3.14);
+    EXPECT_EQ(any_cast<int>(any1), 42);
+
+    EXPECT_EQ(typeid(int),    any1.type());
+    EXPECT_EQ(typeid(double), any2.type());
+}
+
+TEST(any, constness) {
+}
+
+// These should fail at compile time if the constraint that the contents of any
+// satisfy CopyConstructable. This implementation is rock solid, so they have
+// to be commented out.
+/*
+TEST(any, not_copy_constructable) {
+    util::any a(testing::nocopy<int>(3));
+
+    testing::nocopy<int> x(3);
+    util::any b(std::move(x));
+}
+*/
+
+// test any_cast(any*)
+//   - these have different behavior to any_cast on reference types
+//   - are used by the any_cast on refernce types
+TEST(any, any_cast_ptr) {
+
+    // test that valid pointers are returned for int and std::string types
+
+    util::any ai(42);
+    auto ptr_i = util::any_cast<int>(&ai);
+    EXPECT_EQ(*ptr_i, 42);
+
+    util::any as(std::string("hello"));
+    auto ptr_s = util::any_cast<std::string>(&as);
+    EXPECT_EQ(*ptr_s, "hello");
+
+    // test that exceptions are thrown for invalid casts
+    EXPECT_EQ(util::any_cast<int>(&as), nullptr);
+    EXPECT_EQ(util::any_cast<std::string>(&ai), nullptr);
+    util::any empty;
+    EXPECT_EQ(util::any_cast<int>(&empty), nullptr);
+    EXPECT_EQ(util::any_cast<int>((util::any*)nullptr), nullptr);
+
+    // Check that constness of the returned pointer matches that the input.
+    {
+        util::any a(42);
+        auto p = util::any_cast<int>(&a);
+        static_assert(std::is_same<int*, decltype(p)>::value,
+                "any_cast(any*) should not return const*");
+    }
+    {
+        const util::any a(42);
+        auto p = util::any_cast<int>(&a);
+        static_assert(std::is_same<const int*, decltype(p)>::value,
+                "any_cast(const any*) should return const*");
+    }
+}
+
+TEST(any, any_cast_ref) {
+    util::any ai(42);
+    auto i = util::any_cast<int>(ai);
+    EXPECT_EQ(typeid(i), typeid(int));
+    EXPECT_EQ(i, 42);
+}
+
+// test any_cast(any&&)
+TEST(any, any_cast_rvalue) {
+    auto moved = util::any_cast<moveable>(util::any(moveable()));
+    EXPECT_EQ(moved.moves, 2);
+    EXPECT_EQ(moved.copies, 0);
+}
+
+TEST(any, std_swap) {
+    util::any a1(42);
+    util::any a2(3.14);
+
+    auto pi = util::any_cast<int>(&a1);
+    auto pd = util::any_cast<double>(&a2);
+
+    std::swap(a1, a2);
+
+    // test that values were swapped
+    EXPECT_EQ(util::any_cast<int>(a2), 42);
+    EXPECT_EQ(util::any_cast<double>(a1), 3.14);
+
+    // test that underlying pointers did not change
+    EXPECT_EQ(pi, util::any_cast<int>(&a2));
+    EXPECT_EQ(pd, util::any_cast<double>(&a1));
+}
+
+// test operator=(const any&)
+TEST(any, assignment_from_lvalue) {
+    using std::string;
+
+    auto str1 = string("one");
+    auto str2 = string("two");
+    util::any a(str1);
+
+    util::any b;
+    b = a; // copy assignment
+
+    // verify that b contains value stored in a
+    EXPECT_EQ(str1, util::any_cast<string>(b));
+
+    // change the value stored in b
+    *util::any_cast<string>(&b) = str2;
+
+    // verify that a is unchanged and that b holds new value
+    EXPECT_EQ(str1, util::any_cast<string>(a));
+    EXPECT_EQ(str2, util::any_cast<string>(b));
+}
+
+// test operator=(any&&)
+TEST(any, assignment_from_rvalue) {
+    using std::string;
+
+    auto str1 = string("one");
+    auto str2 = string("two");
+    util::any a(str1);
+
+    util::any b;
+    b = std::move(a); // move assignment
+
+    EXPECT_EQ(str1, util::any_cast<string>(b));
+
+    EXPECT_EQ(nullptr, util::any_cast<string>(&a));
+}
+
+// test template<typename T> operator=(T&&)
+TEST(any, assignment_from_value) {
+    std::vector<int> tmp{1, 2, 3};
+
+    // take a pointer to the orignal data to later verify
+    // that the value was moved, and not copied.
+    auto ptr = tmp.data();
+
+    util::any a;
+    a = std::move(tmp);
+
+    auto vec = util::any_cast<std::vector<int>>(&a);
+
+    // ensure the value was moved
+    EXPECT_EQ(ptr, vec->data());
+
+    // ensure that the contents of the vector are unchanged
+    std::vector<int> ref{1, 2, 3};
+    EXPECT_EQ(ref, *vec);
+}
+
+TEST(any, make_any) {
+    using util::make_any;
+    using util::any_cast;
+
+    {
+        auto a = make_any<int>(42);
+
+        EXPECT_EQ(typeid(int), a.type());
+        EXPECT_EQ(42, any_cast<int>(a));
+    }
+
+    // check casting
+    {
+        auto a = make_any<double>(42u);
+
+        EXPECT_EQ(typeid(double), a.type());
+        EXPECT_EQ(42.0, any_cast<double>(a));
+    }
+
+    // check forwarding of parameters to constructor
+    {
+        // create a string from const char*
+        auto a = make_any<std::string>("hello");
+
+        EXPECT_EQ(any_cast<std::string>(a), std::string("hello"));
+    }
+
+    // test that we make_any correctly forwards rvalue arguments to the constructor
+    // of the contained object.
+    {
+        std::vector<int> tmp{1, 2, 3};
+
+        // take a pointer to the orignal data to later verify
+        // that the value was moved, and not copied.
+        auto ptr = tmp.data();
+
+        auto a = make_any<std::vector<int>>(std::move(tmp));
+
+        auto vec = any_cast<std::vector<int>>(&a);
+
+        // ensure the value was moved
+        EXPECT_EQ(ptr, vec->data());
+
+        // ensure that the contents of the vector are unchanged
+        std::vector<int> ref{1, 2, 3};
+        EXPECT_EQ(ref, *vec);
+    }
+
+}