Skip to content

Instantly share code, notes, and snippets.

@jonwis
Created September 26, 2023 18:59
Show Gist options
  • Save jonwis/18667f2aabb40e33ecb5701de58b7f5d to your computer and use it in GitHub Desktop.
Save jonwis/18667f2aabb40e33ecb5701de58b7f5d to your computer and use it in GitHub Desktop.
Interface cache implementation
#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