Created
September 26, 2023 18:59
-
-
Save jonwis/18667f2aabb40e33ecb5701de58b7f5d to your computer and use it in GitHub Desktop.
Interface cache implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <mutex> | |
struct guid { uint64_t id[2]; }; | |
template<typename T> guid const& guid_of() | |
{ | |
return T::iid; | |
} | |
struct IUnknown { | |
virtual uint32_t AddRef() = 0; | |
virtual uint32_t Release() = 0; | |
virtual uint32_t QueryInterface(guid const& id, void** pv) = 0; | |
constexpr static const guid iid{0,1}; | |
}; | |
struct IFoo : IUnknown { | |
virtual uint32_t Do() = 0; | |
virtual uint32_t Dont() = 0; | |
constexpr static const guid iid{0,2}; | |
}; | |
struct IFoo2 : IUnknown { | |
virtual uint32_t Other() = 0; | |
virtual uint32_t Thing() = 0; | |
constexpr static const guid iid{0,3}; | |
}; | |
struct IHidden { | |
virtual uint32_t Moo() = 0; | |
constexpr static const guid iid{0,4}; | |
}; | |
template<typename T, typename U, typename... Ts> constexpr int type_index(int x = 0) | |
{ | |
if constexpr (std::is_same_v<T, U>) | |
{ | |
return x; | |
} | |
else | |
{ | |
return type_index<T, Ts...>(x + 1); | |
} | |
} | |
template<typename... T> struct iface_cache | |
{ | |
IUnknown* cached[sizeof...(T)]{}; | |
template<typename Q> __declspec(noinline) Q* get_instance() { | |
auto& slot = cached[type_index<Q, T...>()]; | |
if (!slot) | |
{ | |
cached[0]->QueryInterface(guid_of<Q>(), reinterpret_cast<void**>(&slot)); | |
} | |
return static_cast<Q*>(slot); | |
} | |
iface_cache(IUnknown* pBase) { cached[0] = pBase; pBase->AddRef(); } | |
~iface_cache() { | |
for (auto const& p : cached) { | |
if (p) | |
p->Release(); | |
} | |
} | |
}; | |
template<typename T, typename... Ts> struct first_type { | |
using type = T; | |
}; | |
template<typename... T> struct smart_iface_cache | |
{ | |
using cache_t = iface_cache<T...>; | |
using base_iface_t = first_type<T...>::type; | |
union alias_t { | |
cache_t* cache; | |
base_iface_t* ptr; | |
uintptr_t alias; | |
}; | |
static_assert(sizeof(alias_t) == sizeof(void*)); | |
alias_t storage{}; | |
static bool is_cache(alias_t const& s) noexcept { return s.alias & 0x1; } | |
~smart_iface_cache() | |
{ | |
if (auto c = as_cache()) | |
{ | |
delete c; | |
} | |
else if (storage.ptr) | |
{ | |
storage.ptr->Release(); | |
} | |
} | |
cache_t* as_cache() | |
{ | |
alias_t tmp = storage; | |
if (is_cache(tmp)) | |
{ | |
tmp.alias &= ~0x1; | |
return tmp.cache; | |
} | |
else | |
{ | |
return nullptr; | |
} | |
} | |
template<typename Q> Q* as() | |
{ | |
// If this instance is a cache, then get it and ask it for | |
// the instance. When the desired interface is the base iface | |
// return it directly. Or potentially convert from single | |
// iface mode to group mode. | |
if (auto c = as_cache()) | |
{ | |
return c->get_instance<Q>(); | |
} | |
else if (storage.ptr) | |
{ | |
if constexpr (std::is_same_v<Q, base_iface_t>) | |
{ | |
return static_cast<Q*>(storage.ptr); | |
} | |
else | |
{ | |
convert_to_cache(); | |
return as_cache()->get_instance<Q>(); | |
} | |
} | |
else | |
{ | |
return nullptr; | |
} | |
} | |
void convert_to_cache() | |
{ | |
storage.cache = new cache_t(storage.ptr); | |
storage.alias |= 0x01; | |
} | |
}; | |
void f(IFoo* pf) { | |
iface_cache<IFoo, IFoo2> cache(pf); | |
cache.get_instance<IFoo2>()->Thing(); | |
cache.get_instance<IFoo2>()->Thing(); | |
cache.get_instance<IFoo>()->Dont(); | |
} | |
void b(smart_iface_cache<IFoo, IFoo2>& pf) { | |
pf.as<IFoo2>()->Thing(); | |
pf.as<IFoo2>()->Thing(); | |
pf.as<IFoo>()->Dont(); | |
} | |
struct Foo : smart_iface_cache<IFoo, IFoo2> { | |
auto Do() { return as<IFoo>()->Do(); } | |
auto Dont() { return as<IFoo>()->Dont(); } | |
auto Other() { return as<IFoo2>()->Other(); } | |
auto Thing() { return as<IFoo2>()->Thing(); } | |
private: | |
auto& get_cache() { return static_cast<smart_iface_cache<IFoo, IFoo2>&>(*this); }; | |
}; | |
void moop(Foo& f) { | |
f.Do(); | |
f.Dont(); | |
f.Other(); | |
f.Thing(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment