Skip to content

Twon/cc-protocol

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

protocol: Structural Subtyping for C++

Language Standard License

An experimental C++ library and generation tool for enabling static structural subtyping (duck typing) with value semantics.

Overview

Polymorphic interfaces in C++ traditionally require explicit inheritance from an abstract base class. This nominal subtyping tightly couples independent components, makes it impossible to retroactively apply interfaces to third-party types, and typically forces reference semantics (e.g., std::unique_ptr).

Inspired by Python's Protocol (PEP 544), this repository explores bringing a similar paradigm to C++. By utilizing AST parsing (via Clang) and code generation, we can automatically synthesize type-erased wrappers that accept any type structurally conforming to an interface.

Crucially, these protocols maintain deep-copy value semantics, strict const-propagation, and allocator awareness, aligning heavily with the principles of jbcoe/value_types (P3019).

Standardization

As C++ reflection (P2996) matures and advanced code injection capabilities are added in future standards (C++29+), the generation process demonstrated here via py_cppmodel will be achievable natively within the language.

A draft proposal detailing this feature can be found in proposals/DRAFT.md.

Use

Unlike traditional polymorphism, the interface is just a struct. No virtual keywords, no = 0, and no base classes.

#pragma once
#include <string>
#include <vector>

namespace xyz {

struct B {
  void process(const std::string& input);
  std::vector<int> get_results() const;
  bool is_ready() const;
};

}  // namespace xyz

Write your concrete type. It does not need to inherit from xyz::B. It only needs to structurally provide the methods defined in the interface.

namespace xyz {

class MyImplementation {
  std::vector<int> results_;
  bool ready_ = false;

 public:
  // Structurally matches xyz::B
  void process(const std::string& input) {
    results_.push_back(input.length());
    ready_ = true;
  }
  std::vector<int> get_results() const { return results_; }
  bool is_ready() const { return ready_; }
};

}  // namespace xyz

We can now use xyz::protocol<xyz::B>, an automatically generated type-erased wrapper. It copies deeply, propagates const correctly, and supports custom allocators.

#include "generated/protocol_B.h"

void run_pipeline(xyz::protocol<xyz::B> worker) {
  if (!worker.is_ready()) {
    worker.process("hello protocols");
  }

  for (int result : worker.get_results()) {
    // ...
  }
}

int main() {
  // Construct the protocol in-place with our implementation
  xyz::protocol<xyz::B> p(std::in_place_type<xyz::MyImplementation>);

  run_pipeline(p); // Pass by value!
  return 0;
}

The generated wrapper uses C++20 concepts and requires clauses: any structural mismatch emits clear, pinpointed compile-time errors rather than deeply nested template instantiation failures.

class BadImplementation {
 public:
  void process(const std::string& input);
  // ERROR: Missing get_results()
  // ERROR: is_ready() is missing 'const'
  bool is_ready();
};

// COMPILER ERROR:
// constraints not satisfied
// the required expression 'std::as_const(t).is_ready()' is invalid

Contributing and Development

For instructions on how to build, test, and contribute to this project, as well as a deeper look into the code generation architecture, please refer to the Developer Guide.

References

About

A type erased value-type for C++ using reflection

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • C++ 29.9%
  • CMake 29.5%
  • Jinja 25.5%
  • Python 10.9%
  • Dockerfile 2.5%
  • Shell 1.7%