August 19th, 2025
0 reactions

Announcing Proxy 4: The Next Leap in C++ Polymorphism

Mingxin Wang
Senior Software Engineer

Proxy 4 is here! After years of innovation and sustained use in the Windows OS codebase (since 2022), Proxy has matured from a bold experiment into a production-grade, modern C++ library for runtime polymorphism. The theory behind Proxy is original, and its design is now proven at scale. We are excited to invite the broader C++ community to join us in shaping the future of polymorphism. Your ideas and contributions are more welcome than ever!

A special thank you to @SidneyCogdill for his outstanding contributions, especially for bringing support for C++20 modules to Proxy and driving major‑version compatibility efforts. His work has made the library even more accessible and future‑proof.

What is Proxy?

Proxy is a header-only, cross-platform C++20 library that lets you write polymorphic code without the pain of inheritance or the limitations of traditional virtual functions. Proxy enables you to:

  • Write code that is portable, non-intrusive, and easy to maintain
  • Manage object lifetimes flexibly, with or without ownership
  • Achieve performance that rivals or exceeds hand-written code
  • Compose abstractions from any expression: member functions, free functions, operators, conversions, and more

If you’re new to Proxy, check out our GitHub repository and our previous announcements: Announcing the Proxy 3 Library for Dynamic Polymorphism and Analyzing the Performance of the “Proxy” Library.

New Website: Your One-Stop Reference

We’ve launched a new documentation website, built with MkDocs, to help you find everything you need about Proxy. The new site offers improved navigation, a unified FAQ, and a clear structure for all features and APIs. Explore the documentation and discover how easy it is to get started!

Index

Try Proxy Instantly in Compiler Explorer

You can now experiment with Proxy directly in your browser using Compiler Explorer! Write, run, and inspect Proxy code without any setup. Just click on “Libraries” and search for “proxy” in Compiler Explorer or use a shared link from our examples to get started. It’s a great way to see Proxy in action and share your findings with others.

Compiler Explorer

What’s New in Proxy 4?

Skills: Composable Facade Capabilities

Let’s see how easy it is to add formatting support to your types (run in Compiler Explorer):

#include <format>
#include <proxy/proxy.h>

struct Formattable : pro::facade_builder
    ::add_skill<pro::skills::format>
    ::build {};

int main() {
  pro::proxy<Formattable> p = pro::make_proxy<Formattable>(123);
  std::cout << std::format("{}\n", *p); // Prints "123"
}

Skills are reusable building blocks that let you add powerful capabilities to your facades with a single line:

Skills are easy to compose and extend. You can also author internal, domain‑specific skills to capture patterns (logging, metrics hooks, validation, instrumentation) so teams reuse a single, well-reviewed definition instead of duplicating conventions. This improves consistency and long‑term maintainability. See the skills documentation for more.

Easy Borrowing and Weak References

Proxy 4 introduces convenient aliases for non-owning and weak references: proxy_view and weak_proxy. These are built on top of the core proxy concept, making it easier to express borrowing and weak ownership patterns in your code. For example, you can use proxy_view to safely borrow an object without taking ownership, or weak_proxy to create a weak reference that can be locked when needed. See the proxy_view documentation and weak_proxy documentation for details.

Shared Ownership Made Simple

With the new make_proxy_shared and allocate_proxy_shared APIs, you can create shared and weak proxies efficiently, without the overhead of std::shared_ptr. These APIs use compact internal pointer types, ensuring high performance and low memory usage. Learn more in the make_proxy_shared documentation and allocate_proxy_shared documentation.

Smarter Dispatch and Conversion

Recursive Facade Patterns Made Easy

Proxy 4 introduces facade_aware_overload_t, which lets you define recursive conventions that refer to the facade itself without forcing early instantiation. This is especially useful for operator chaining patterns (like arithmetic or concatenation) that return new proxy objects of the same facade. Example:

#include <format>
#include <iostream>
#include <proxy/proxy.h>

template <class F>
using BinaryOverload =
    pro::proxy<F>(const pro::proxy_indirect_accessor<F>& rhs) const;

template <class T, pro::facade F>
pro::proxy<F> operator+(const T& value,
                        const pro::proxy_indirect_accessor<F>& rhs)
  requires(!std::is_same_v<T, pro::proxy_indirect_accessor<F>>)
{
  return pro::make_proxy<F, T>(value + proxy_cast<const T&>(rhs));
}

struct Addable
    : pro::facade_builder              // Compose capabilities
      ::add_skill<pro::skills::rtti>   // RTTI support
      ::add_skill<pro::skills::format> // Formatting support
      ::add_convention<pro::operator_dispatch<"+">,
                       pro::facade_aware_overload_t<BinaryOverload>> // Recursive operator
      ::build {};

int main() {
  pro::proxy<Addable> p1 = pro::make_proxy<Addable>(1);
  pro::proxy<Addable> p2 = pro::make_proxy<Addable>(2);
  pro::proxy<Addable> p3 = *p1 + *p2; // Uses facade-aware overload
  std::cout << std::format("{}\n", *p3); // Prints "3"
}

See the facade_aware_overload_t documentation for details.

Quality Improvements for Everyone

  • Bitwise Trivially Relocatable: Proxy now supports types that can be moved with memcpy, enabling faster moves and assignments by default. This means better performance and less boilerplate for you. See the is_bitwise_trivially_relocatable documentation for more details.
  • C++20 Modules Support: Thanks to @SidneyCogdill, Proxy now ships with .ixx files for blazing-fast builds and better IDE support.
  • Diagnostics: Error messages are now much clearer and more actionable, helping you quickly understand and fix issues in your code.
  • Debugging Experience: Proxy 4 generates better debugging symbols, making it easier to inspect proxies in your debugger and understand what’s happening under the hood.
  • Accessibility Authoring: It’s now easier to expose reflection and dispatch capabilities in your facades, with less boilerplate and more flexibility.
  • Code Generation: Proxy 4 refines the call path for indirect dispatch, reducing the number of instructions around indirect calls.
  • Compiler Support: NVIDIA HPC is newly added to our supported toolchains. Proxy 4 passes tests on the latest GCC, Clang, MSVC, and NVIDIA HPC compilers. We’re committed to keeping Proxy working with these compilers going forward, including freestanding environments. See the README for minimum versions and flags.

Major Version Compatibility

Upgrading a small component is usually straightforward, but migrating a monorepo or multi-module product can be challenging. Follow the guidelines below:

  1. Minor or patch upgrades (e.g. 3.3.0 → 3.4.0) All 3.x.y releases preserve API/ABI compatibility, so different parts of the program may safely depend on different 3.x.y versions. No special action is required.
  2. Major upgrades (e.g. 3.4.0 → 4.0.0)
    • If your current version is earlier than 3.4.0, migrate to 3.4.0 first.
    • Starting with 3.4.0, each major release is placed in a versioned inline namespace (pro::v3, pro::v4, …).  When a translation unit sees multiple majors, qualify the namespace explicitly:
      pro::v3::foo(); // Proxy 3 API
      pro::v4::foo(); // Proxy 4 API

      The newest release re-exports its namespace as the inline (default) namespace, so unqualified calls (pro::foo()) resolve to the latest version once the migration is complete.

    • The macros also have major-qualified aliases, e.g. PRO4_DEF_MEM_DISPATCH. Use these forms whenever headers from multiple majors are included in the same translation unit.
    • Upgrade subsystems incrementally, module-by-module or DLL-by-DLL. When every target depends only on the new major, drop the old include path and remove the previous version from your build.

These rules let old and new code coexist during the transition while keeping ODR violations at bay.

Other Notable Changes

Summary

Proxy 4 builds on a mature foundation introduced and proven in Proxy 3, and adds focused refinements rather than reinventing the model.

Core capabilities (established in Proxy 3 and still central in day‑to‑day use):

  • Expression‑based polymorphism: define a facade once, make virtually any expression (member calls, free calls, operators, explicit/implicit conversions) polymorphic.
  • Flexible creation: make_proxy (inline where possible), allocate_proxy (custom allocators), raw pointer attachment, unique ownership, and composition of abstractions via add_convention / add_reflection / add_facade.
  • Lifetime versatility in a single proxy type: same facade supports non‑owning, unique, inline, or externally managed objects transparently.
  • Overload resolution & reflection support for concise, generic call sites.

Targeted Proxy 4 refinements:

  • Ergonomic aliases for borrowing / weak observation (proxy_view, weak_proxy) built atop the existing core.
  • Skill-based composition (formatting, RTTI, view/weak conversion, slim layout, {fmt} integration) to replace repetitive convention boilerplate.
  • New shared/weak creation helpers (make_proxy_shared, allocate_proxy_shared) complement (not replace) the more frequently used make_proxy / allocate_proxy paths.
  • Concepts (proxiable_target, inplace_proxiable_target) clarify when a type can be used with make_proxy versus make_proxy_inplace, giving earlier, sharper diagnostics instead of template noise.
  • facade_aware_overload_t enables recursive facade/operator definitions (e.g. arithmetic that returns the same facade) without forcing premature instantiation.
  • weak_dispatch for graceful fallback when some contained types omit a convention.
  • Improved diagnostics, relocation optimizations (is_bitwise_trivially_relocatable), module support, and leaner code generation.

Questions and feedback are welcome! If you find something unclear, a bug, or a documentation gap, feel free to open an issue or submit a PR.

Author

Mingxin Wang
Senior Software Engineer

0 comments