Type-Erased Echo
Echo server demonstrating the compilation firewall pattern.
What You Will Learn
-
Using
any_streamfor transport-independent code -
Physical isolation through separate compilation
-
Build time benefits of type erasure
Prerequisites
-
Completed Mock Stream Testing
-
Understanding of type erasure from Physical Isolation
Source Code
echo.hpp
#ifndef ECHO_HPP
#define ECHO_HPP
#include <boost/capy/io/any_stream.hpp>
#include <boost/capy/task.hpp>
namespace myapp {
// Type-erased interface: no template dependencies
boost::capy::task<> echo_session(boost::capy::any_stream& stream);
} // namespace myapp
#endif
echo.cpp
#include "echo.hpp"
#include <boost/capy/read.hpp>
#include <boost/capy/write.hpp>
namespace myapp {
using namespace boost::capy;
task<> echo_session(any_stream& stream)
{
char buffer[1024];
for (;;)
{
// Read some data
auto [ec, n] = co_await stream.read_some(mutable_buffer(buffer));
if (ec == cond::eof)
co_return; // Client closed connection
if (ec.failed())
throw std::system_error(ec);
// Echo it back
auto [wec, wn] = co_await write(stream, const_buffer(buffer, n));
if (wec.failed())
throw std::system_error(wec);
}
}
} // namespace myapp
main.cpp
#include "echo.hpp"
#include <boost/capy.hpp>
#include <boost/capy/test/stream.hpp>
#include <boost/capy/test/run_blocking.hpp>
#include <iostream>
using namespace boost::capy;
void test_with_mock()
{
test::stream mock;
mock.provide("Hello, ");
mock.provide("World!\n");
mock.provide_eof();
any_stream stream{mock};
test::run_blocking(myapp::echo_session(stream));
std::cout << "Echo output: " << mock.output() << "\n";
}
int main()
{
test_with_mock();
// With real sockets (using Corosio):
// tcp::socket socket;
// ... accept connection ...
// any_stream stream{socket};
// co_await myapp::echo_session(stream);
return 0;
}
Build
add_library(echo_lib echo.cpp)
target_link_libraries(echo_lib PUBLIC capy)
add_executable(echo_demo main.cpp)
target_link_libraries(echo_demo PRIVATE echo_lib)
Walkthrough
The Interface
// echo.hpp
task<> echo_session(any_stream& stream);
The header declares only the signature. It includes any_stream and task, but no concrete transport types.
Clients of this header:
-
Can call
echo_sessionwith any stream -
Do not depend on implementation details
-
Do not recompile when implementation changes
Exercises
-
Add logging to
echo_sessionand observe that clients don’t recompile -
Create a second implementation file with different behavior (e.g., uppercase echo)
-
Measure compile times with and without type erasure in a larger project
Next Steps
-
Timeout with Cancellation — Stop tokens for timeout