Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9abaf09
Share pinterface calculation code with cppwinrt and precalc generic i…
DefaultRyan May 22, 2026
b9e9b02
Replace sprintf with a quick lookup table for hex digits
DefaultRyan May 22, 2026
e8b7167
Minor cleanup
DefaultRyan May 27, 2026
f5bab1f
Fix some calculations. Wrap guid_v and name_v specializations in prep…
DefaultRyan May 27, 2026
7c631fe
Precompute "standard" IReference<T> pinterfaces
DefaultRyan May 27, 2026
765f429
Fix shift-left underflow
DefaultRyan May 27, 2026
41e5bc0
Specialize pinterface_guid instead of guid_v. Friendlier to modules. …
DefaultRyan May 28, 2026
512a6d4
Wrap namespace and ifndef
DefaultRyan May 28, 2026
e94050f
Move manual IReference specializations from base_reference_produce in…
DefaultRyan May 28, 2026
5e58472
Typo in IReference<guid>
DefaultRyan May 28, 2026
81d2d02
Apply CoPilot suggestions from code review
DefaultRyan May 29, 2026
f8a9002
Synthesize GenericTypeInstSig for IReference specializations, instead…
DefaultRyan May 29, 2026
758a0a1
Remove leftover name_v machinery
DefaultRyan May 29, 2026
8ba0c2e
Update winmd nuget package to 1.0.260529.3 to fix build break
DefaultRyan May 29, 2026
68d98ab
Fix i686 cross-build by avoiding removed winmd signature constructors
Copilot May 30, 2026
9b1d5ee
Apply remaining changes
Copilot May 30, 2026
4780fb0
The problem was a pinned old version of winmd in the cmake files.
DefaultRyan May 30, 2026
0b2ac26
Merge branch 'master' into user/defaultryan/pinterface_precalc
DefaultRyan Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions cppwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,23 @@ namespace cppwinrt
return { w, write_close_namespace };
}

[[nodiscard]] static finish_with wrap_extern_cpp_impl_namespace(writer& w)
{
w.write(R"(
extern "C++"
{
namespace winrt::impl
{
)");

return { w, [](writer& w) {
w.write(R"(
} // winrt::impl
} // extern "C++"
)");
}};
}

[[nodiscard]] static finish_with wrap_std_namespace(writer& w)
{
w.write(R"(namespace std
Expand Down Expand Up @@ -537,6 +554,58 @@ namespace cppwinrt
}
}

static std::string make_pinterface_guard(std::string_view const& cpp_name)
{
std::string guard = "WINRT_IMPL_PINTERFACE_GUID_";
guard.reserve(guard.size() + cpp_name.size());
for (char c : cpp_name)
{
if (c == ':' || c == '<' || c == '>' || c == ',' || c == ' ')
guard += '_';
else
guard += c;
}
return guard;
}

static void write_generic_inst_specializations(writer& w, std::map<std::string, generic_inst_info> const& instantiations)
{
if (instantiations.empty())
{
return;
}

// This block is self-contained (written outside any open namespace).
// extern "C++" attaches specializations to the global module so identical
// specializations across modules merge rather than collide.
// #ifndef guards prevent redefinition within a single TU (SCC-consolidated modules).
auto wrap_ns = wrap_extern_cpp_impl_namespace(w);

for (auto&& [winrt_name, info] : instantiations)
{
auto guard = make_pinterface_guard(info.cpp_name);
{
auto wrap_guard = wrap_ifndef(w, guard);
w.write("#define %\n", guard);
w.write(" template <> struct pinterface_guid<%>\n", info.cpp_name);
w.write(" {\n");
w.write(" static constexpr bool precomputed = true;\n");
w.write(" static constexpr guid value{ ");
auto& g = info.guid;
w.write_printf("0x%08X,0x%04X,0x%04X,{ 0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X }",
g.Data1, g.Data2, g.Data3,
g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3],
g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]);
w.write(" }; // ");
w.write_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
g.Data1, g.Data2, g.Data3,
g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3],
g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]);
w.write(" };\n");
}
}
}

static void write_struct_category(writer& w, TypeDef const& type)
{
auto format = R"( template <> struct category<%>{ using type = struct_category<%>; };
Expand Down
10 changes: 10 additions & 0 deletions cppwinrt/file_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,22 @@ namespace cppwinrt
w.write_each<write_guid>(members.interfaces);
w.write_each<write_guid>(members.delegates);
w.write_each<write_default_interface>(members.classes);

w.write_each<write_interface_abi>(members.interfaces);
w.write_each<write_delegate_abi>(members.delegates);
w.write_each<write_consume>(members.interfaces);
w.write_each<write_struct_abi>(members.structs);
}

// Emit pinterface_guid specializations AFTER the wrap_impl_namespace block
// so they don't break the WINRT_EXPORT attribute on subsequent declarations.
// The function writes a self-contained extern "C++" block.
{
std::map<std::string, generic_inst_info> instantiations;
collect_generic_instantiations(w, members, instantiations);
write_generic_inst_specializations(w, instantiations);
}

write_close_file_guard(w);
w.swap();
write_preamble(w);
Expand Down
279 changes: 279 additions & 0 deletions cppwinrt/helpers.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
#pragma once

#include "winmd_signature.h"

namespace cppwinrt
{
using winmd_signature::guid_value;
using winmd_signature::extract_guid;
using winmd_signature::format_guid_signature;
using winmd_signature::compute_guid_from_signature;
using winmd_signature::type_arg_stack;
using winmd_signature::signature_builder;

static auto get_start_time()
{
return std::chrono::high_resolution_clock::now();
Expand Down Expand Up @@ -1145,4 +1154,274 @@ namespace cppwinrt

return settings.component_filter.includes(class_name);
}

struct generic_inst_info
{
std::string cpp_name;
guid_value guid;
};

// ---- winrt_name_builder: builds WinRT display names from metadata ----
struct winrt_name_builder
{
static std::string get_name(GenericTypeInstSig const& type, type_arg_stack const& resolve = {})
{
auto generic_type = type.GenericType();
auto [ns, name] = get_type_namespace_and_name(generic_type);

std::string result = std::string(ns) + "." + std::string(name) + "<";
bool first = true;
for (auto&& arg : type.GenericArgs())
{
if (!first) result += ", ";
first = false;
result += get_arg_name(arg, resolve);
}
result += ">";
return result;
}

private:
static std::string get_element_name(ElementType t)
{
switch (t)
{
case ElementType::Boolean: return "Boolean";
case ElementType::Char: return "Char16";
case ElementType::I1: return "Int8";
case ElementType::U1: return "UInt8";
case ElementType::I2: return "Int16";
case ElementType::U2: return "UInt16";
case ElementType::I4: return "Int32";
case ElementType::U4: return "UInt32";
case ElementType::I8: return "Int64";
case ElementType::U8: return "UInt64";
case ElementType::R4: return "Single";
case ElementType::R8: return "Double";
case ElementType::String: return "String";
case ElementType::Object: return "Object";
default: return {};
}
}

static std::string get_typedef_name(TypeDef const& td)
{
return std::string(td.TypeNamespace()) + "." + std::string(td.TypeName());
}

static std::string get_arg_name(TypeSig const& sig, type_arg_stack const& resolve)
{
return call(sig.Type(),
[](ElementType t) -> std::string { return get_element_name(t); },
[&](GenericTypeIndex idx) -> std::string
{
if (!resolve.empty() && idx.index < resolve.back().size())
{
type_arg_stack parent_resolve(resolve.begin(), resolve.end() - 1);
return get_arg_name(resolve.back()[idx.index], parent_resolve);
}
return {};
},
[](GenericMethodTypeIndex) -> std::string { return {}; },
[&](coded_index<TypeDefOrRef> const& t) -> std::string
{
switch (t.type())
{
case TypeDefOrRef::TypeDef: return get_typedef_name(t.TypeDef());
case TypeDefOrRef::TypeRef:
{
auto tr = t.TypeRef();
if (tr.TypeNamespace() == "System" && tr.TypeName() == "Guid") return "Guid";
return get_typedef_name(find_required(tr));
}
default: return get_name(t.TypeSpec().Signature().GenericTypeInst(), resolve);
}
},
[&](GenericTypeInstSig const& t) -> std::string { return get_name(t, resolve); });
}
};

// ---- Recursive collection of concrete generic instantiations ----
// Walks InterfaceImpl chains and method signatures, carrying concrete TypeSig args to resolve GenericTypeIndex.

static void collect_generic_inst_recursive(
writer& w,
GenericTypeInstSig const& type,
type_arg_stack const& outer_resolve,
std::map<std::string, generic_inst_info>& instantiations);

// Helper: if a TypeSig contains a GenericTypeInstSig (directly or via TypeSpec), collect it.
static void collect_from_type_sig(
writer& w,
TypeSig const& sig,
type_arg_stack const& resolve,
std::map<std::string, generic_inst_info>& instantiations)
{
call(sig.Type(),
[](ElementType) {},
[&](GenericTypeIndex idx)
{
// Resolve and recurse if the resolved type is itself a generic instantiation
if (!resolve.empty() && idx.index < resolve.back().size())
{
type_arg_stack parent_resolve(resolve.begin(), resolve.end() - 1);
collect_from_type_sig(w, resolve.back()[idx.index], parent_resolve, instantiations);
}
},
[](GenericMethodTypeIndex) {},
[&](coded_index<TypeDefOrRef> const& t)
{
if (t.type() == TypeDefOrRef::TypeSpec)
{
collect_generic_inst_recursive(w, t.TypeSpec().Signature().GenericTypeInst(), resolve, instantiations);
}
},
[&](GenericTypeInstSig const& t)
{
collect_generic_inst_recursive(w, t, resolve, instantiations);
});
}

static void collect_generic_inst_recursive(
writer& w,
GenericTypeInstSig const& type,
type_arg_stack const& outer_resolve,
std::map<std::string, generic_inst_info>& instantiations)
{
// Build the resolution context for this instantiation:
// The args of 'type' may themselves contain GenericTypeIndex references
// that need resolving through outer_resolve.
// Collect the concrete TypeSig args after resolution.
std::vector<TypeSig> concrete_args;
for (auto&& arg : type.GenericArgs())
{
if (auto* idx = std::get_if<GenericTypeIndex>(&arg.Type()))
{
// Resolve through the outer stack
if (outer_resolve.empty() || idx->index >= outer_resolve.back().size())
{
return; // Can't resolve — open generic, skip
}
concrete_args.push_back(outer_resolve.back()[idx->index]);
}
else
{
concrete_args.push_back(arg);
}
}

// Build a resolution stack with our concrete args appended
type_arg_stack resolve = outer_resolve;
resolve.push_back(concrete_args);

// Use winrt_name as the dedup key. Only do expensive work if this is a new entry.
auto winrt_name = winrt_name_builder::get_name(type, outer_resolve);
auto [it, inserted] = instantiations.try_emplace(std::move(winrt_name));
if (!inserted)
{
return;
}

it->second.cpp_name = w.write_temp("%", type);
it->second.guid = compute_guid_from_signature(signature_builder::get_signature(type, outer_resolve));

// Recurse into generic args that are themselves generic instantiations
for (auto&& arg : concrete_args)
{
if (auto* spec = std::get_if<coded_index<TypeDefOrRef>>(&arg.Type()))
{
if (spec->type() == TypeDefOrRef::TypeSpec)
{
collect_generic_inst_recursive(w, spec->TypeSpec().Signature().GenericTypeInst(), outer_resolve, instantiations);
}
}
else if (auto* inst = std::get_if<GenericTypeInstSig>(&arg.Type()))
{
collect_generic_inst_recursive(w, *inst, outer_resolve, instantiations);
}
}

// Recurse into the generic type's required interfaces (e.g., IMap : IIterable, etc.)
auto base_type = find_required(type.GenericType());

// Push the writer's generic_param_stack so write_temp resolves GenericTypeIndex in nested calls
auto writer_guard = w.push_generic_params(type);

for (auto&& impl : base_type.InterfaceImpl())
{
auto iface = impl.Interface();
if (iface.type() == TypeDefOrRef::TypeSpec)
{
// This TypeSpec may have GenericTypeIndex references to base_type's params.
// 'resolve' has our concrete args at the back, so signature_builder can resolve them.
collect_generic_inst_recursive(w, iface.TypeSpec().Signature().GenericTypeInst(), resolve, instantiations);
}
}

// Walk method signatures to find generic types in return types and parameters.
// E.g., IIterable<T>.First() returns IIterator<T>, so IIterable<IKeyValuePair<K,V>>
// produces IIterator<IKeyValuePair<K,V>> which needs name_v/guid_v.
for (auto&& method : base_type.MethodList())
{
auto sig = method.Signature();
if (sig.ReturnType())
{
collect_from_type_sig(w, sig.ReturnType().Type(), resolve, instantiations);
}
for (auto&& param : sig.Params())
{
collect_from_type_sig(w, param.Type(), resolve, instantiations);
}
}
}

// Entry point: collect all concrete generic instantiations from a namespace's members.
static void collect_generic_instantiations(
writer& w,
cache::namespace_members const& members,
std::map<std::string, generic_inst_info>& instantiations)
{
auto collect_from_type = [&](TypeDef const& type)
{
for (auto&& impl : type.InterfaceImpl())
{
auto iface = impl.Interface();
if (iface.type() == TypeDefOrRef::TypeSpec)
{
collect_generic_inst_recursive(w, iface.TypeSpec().Signature().GenericTypeInst(), {}, instantiations);
}
}

// Also walk the type's own methods for concrete generic return/param types.
for (auto&& method : type.MethodList())
{
auto sig = method.Signature();
if (sig.ReturnType())
{
collect_from_type_sig(w, sig.ReturnType().Type(), {}, instantiations);
}
for (auto&& param : sig.Params())
{
collect_from_type_sig(w, param.Type(), {}, instantiations);
}
}
};

for (auto&& type : members.classes)
{
collect_from_type(type);
for (auto&& base : get_bases(type))
{
collect_from_type(base);
}
}

for (auto&& type : members.interfaces)
{
if (empty(type.GenericParam()))
{
collect_from_type(type);
}
}
}
}
Loading
Loading