Commit 8ed2344b authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

StaticSingletonManager type-erased create

Summary: `StaticSingletonManager::create<T, Tag>` is larger than necessary, which may not be desirable when using many tags with the same type. Permit call-sites to use static data to shrink the functions.

Reviewed By: aary

Differential Revision: D27863037

fbshipit-source-id: 9f6ced3e7d7a85b78e671f69d7a475ab4de5eea0
parent 6fcad841
......@@ -33,7 +33,7 @@ class StaticSingletonManagerWithRttiImpl {
static void* create(Arg& arg) {
// This Leaky Meyers Singleton must always live in the .cpp file.
static auto& instance = *new StaticSingletonManagerWithRttiImpl();
auto const ptr = instance.entry(*arg.key).get(arg.make);
auto const ptr = instance.entry(*arg.key).get(*arg.make);
arg.cache.store(ptr, std::memory_order_release);
return ptr;
}
......
......@@ -32,20 +32,67 @@ namespace detail {
// Does not support dynamic loading but works without rtti.
class StaticSingletonManagerSansRtti {
private:
using Cache = std::atomic<void*>;
using Make = void*();
struct Arg {
Cache cache{}; // should be first field
Make* make;
template <typename T, typename Tag>
/* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept
: make{thunk::make<T>} {}
};
template <bool Noexcept>
static void* create_(Arg&) noexcept(Noexcept); // no defn; only for decltype
template <bool Noexcept>
using Create = decltype(create_<Noexcept>);
public:
template <bool Noexcept>
struct ArgCreate : private Arg {
friend class StaticSingletonManagerSansRtti;
template <typename T, typename Tag>
/* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept
: Arg{t}, create{create_<T, Tag>} {
static_assert(Noexcept == noexcept(T()), "mismatched noexcept");
}
private:
Create<Noexcept>* create;
};
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
static std::atomic<T*> cache{};
auto const pointer = cache.load(std::memory_order_acquire);
return FOLLY_LIKELY(!!pointer) ? *pointer : create_<T, Tag>(cache);
static Arg arg{tag<T, Tag>};
return create<T, Tag>(arg);
}
template <typename T, typename..., bool Noexcept = noexcept(T())>
FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : arg.create(arg);
return *static_cast<T*>(p);
}
private:
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_NOINLINE static T& create_(std::atomic<T*>& cache) {
FOLLY_ERASE static T& create(Arg& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<T, Tag>(arg);
return *static_cast<T*>(p);
}
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(
noexcept(T())) {
auto& cache = arg.cache;
static Indestructible<T> instance;
cache.store(&*instance, std::memory_order_release);
return *instance;
return &*instance;
}
};
......@@ -56,30 +103,63 @@ class StaticSingletonManagerSansRtti {
//
// Supports dynamic loading but requires rtti.
class StaticSingletonManagerWithRtti {
public:
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
constexpr auto& make = thunk::make<T>;
// gcc and clang behave poorly if typeid is hidden behind a non-constexpr
// function, but typeid is not constexpr under msvc
static Arg arg{{nullptr}, FOLLY_TYPE_INFO_OF(tag_t<T, Tag>), make};
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);
return *static_cast<T*>(p);
}
private:
using Key = std::type_info;
using Make = void*();
using Cache = std::atomic<void*>;
struct Arg {
Cache cache; // should be first field
Cache cache{}; // should be first field
Key const* key;
Make& make;
Make* make;
// gcc and clang behave poorly if typeid is hidden behind a non-constexpr
// function, but typeid is not constexpr under msvc
template <typename T, typename Tag>
/* implicit */ constexpr Arg(tag_t<T, Tag>) noexcept
: key{FOLLY_TYPE_INFO_OF(tag_t<T, Tag>)}, make{thunk::make<T>} {}
};
template <bool Noexcept>
FOLLY_NOINLINE static void* create_(Arg& arg) noexcept(Noexcept);
FOLLY_NOINLINE static void* create_(Arg&) noexcept(Noexcept);
template <bool Noexcept>
using Create = decltype(create_<Noexcept>);
public:
template <bool Noexcept>
struct ArgCreate : private Arg {
friend class StaticSingletonManagerWithRtti;
template <typename T, typename Tag>
/* implicit */ constexpr ArgCreate(tag_t<T, Tag> t) noexcept
: Arg{t}, create{create_<Noexcept>} {
static_assert(Noexcept == noexcept(T()), "mismatched noexcept");
}
private:
Create<Noexcept>* create;
};
template <typename T, typename Tag>
FOLLY_EXPORT FOLLY_ALWAYS_INLINE static T& create() {
static Arg arg{tag<T, Tag>};
return create<T, Tag>(arg);
}
template <typename T, typename..., bool Noexcept = noexcept(T())>
FOLLY_ERASE static T& create(ArgCreate<Noexcept>& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : arg.create(arg);
return *static_cast<T*>(p);
}
private:
template <typename T, typename Tag>
FOLLY_ERASE static T& create(Arg& arg) {
auto const v = arg.cache.load(std::memory_order_acquire);
auto const p = FOLLY_LIKELY(!!v) ? v : create_<noexcept(T())>(arg);
return *static_cast<T*>(p);
}
};
using StaticSingletonManager = std::conditional_t<
......
......@@ -67,6 +67,11 @@ TEST_F(StaticSingletonManagerTest, example_sans_rtti) {
auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k);
static K::ArgCreate<true> m_arg{tag<T, Tag<int>>};
auto& m = K::create<T>(m_arg);
EXPECT_NE(&i, &m);
EXPECT_EQ(T::value, m);
}
TEST_F(StaticSingletonManagerTest, example_with_rtti) {
......@@ -84,6 +89,11 @@ TEST_F(StaticSingletonManagerTest, example_with_rtti) {
auto& k = K::create<T, Tag<char*>>();
EXPECT_NE(&i, &k);
EXPECT_EQ(T::value, k);
static K::ArgCreate<true> m_arg{tag<T, Tag<int>>};
auto& m = K::create<T>(m_arg);
EXPECT_NE(&i, &m);
EXPECT_EQ(T::value, m);
}
TEST_F(StaticSingletonManagerTest, example) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment