cppdap: C++ Implementation of the Debug Adapter Protocol
repository·main·Indexed 41 days ago
https://github.com/google/cppdapA C++11 library providing type-safe structures and a type-safe API for building Debug Adapter Protocol (DAP) clients and servers. It includes full DAP specification coverage, support for custom protocol messages, and flexible JSON serialization options including nlohmann/json, RapidJSON, and JsonCpp.
What's inside cppdap
- cppdap is a C++11 library that implements the Debug Adapter Protocol (DAP). It provides a type-safe API for developers to implement either a DAP client or a DAP server. The library includes C++ structures that cover the full DAP specification and allows for the addition of custom protocol messages.
Build cppdap on Linux and macOS
mainTo build
cppdapon Linux or macOS, use CMake to generate build files and then usemake.- Create a build directory and run CMake:
mkdir build cd build cmake .. [FLAGS] - Build the project:
make
Available CMake flags:
-DCPPDAP_BUILD_TESTS=1: Builds thecppdapunit tests.-DCPPDAP_BUILD_EXAMPLES=1: Builds thecppdapexamples.-DCPPDAP_INSTALL_VSCODE_EXAMPLES=1: Installs examples as Visual Studio Code extensions.-DCPPDAP_WARNINGS_AS_ERRORS=1: Treats all compiler warnings as errors.
cd <path-to-cppdap> mkdir build cd build cmake .. -DCPPDAP_BUILD_TESTS=1 make- Create a build directory and run CMake:
Fetch dependencies for cppdap
maincppdap uses git submodules for its primary dependencies:
nlohmann/jsonandgoogletest. You must initialize these submodules before building.To fetch the dependencies, run:
cd <path-to-cppdap> git submodule update --initBuild cppdap on Windows
mainOn Windows, you can buildcppdapusing Visual Studio 2019's CMake integration.Integrate cppdap into your CMake project
mainYou can include
cppdapin your own project usingadd_subdirectory(). This defines thecppdaplibrary target, which you can then link to your own targets.Basic Integration
set(CPPDAP_DIR <path-to-cppdap>) add_subdirectory(${CPPDAP_DIR}) target_link_libraries(<target> cppdap)Customizing Dependency Paths
You can specify custom paths for third-party libraries by setting these variables before calling
add_subdirectory():CPPDAP_THIRD_PARTY_DIR: The root directory for third-party libraries (defaults to${CPPDAP_DIR}/third_party).CPPDAP_JSON_DIR: Path to the JSON library (defaults to${CPPDAP_THIRD_PARTY_DIR}/json).CPPDAP_GOOGLETEST_DIR: Path togoogletest(defaults to${CPPDAP_THIRD_PARTY_DIR}/googletest).
set(CPPDAP_DIR <path-to-cppdap>) set(CPPDAP_THIRD_PARTY_DIR <third-party-root-directory>) set(CPPDAP_JSON_DIR <path-to-nlohmann-json>) set(CPPDAP_GOOGLETEST_DIR <path-to-googletest>) add_subdirectory(${CPPDAP_DIR}) target_link_libraries(<target> cppdap)Configure JSON library for cppdap
mainWhile
cppdapdefaults tonlohmann/json, you can use alternative JSON serialization libraries by setting the following CMake cache variables:CPPDAP_USE_EXTERNAL_NLOHMANN_JSON_PACKAGECPPDAP_USE_EXTERNAL_RAPIDJSON_PACKAGE(forRapidJSON)CPPDAP_USE_EXTERNAL_JSONCPP_PACKAGE(forJsonCpp)
Connect a Session to a Reader and Writer
mainAfter creating a session, you must bind it to communication channels using
connect(). This method requires astd::shared_ptr<dap::Reader>and astd::shared_ptr<dap::Writer>. Once connected, the session can begin receiving and sending DAP messages.session->connect(reader, writer);Send a DAP Request
mainTo send a command to the debug adapter, use the
sendoverload that acceptsTypeInfofor both the request and the expected response. This method automatically manages sequence numbers and registers aGenericResponseHandlerto catch the eventual response.Arguments:
requestTypeInfo: Thedap::TypeInfodescribing the request structure.responseTypeInfo: Thedap::TypeInfodescribing the expected response structure.request: A pointer to the data object to be serialized.responseHandler: A callback invoked when the response arrives.
session->send(requestTypeInfo, responseTypeInfo, requestData, [](const void* data, const dap::Error* error) { if (error) { // Handle error } else { // Handle successful response data } });Write content using ContentWriter
mainThe
ContentWriterclass provides an interface for writing data to adap::Writer. It is initialized with astd::shared_ptr<Writer>.Key methods:
isOpen(): Returnstrueif the writer is currently open.close(): Closes the writer.write(const std::string&): Writes the provided string to the underlying writer. Returnstrueif successful.
#include "dap/content_stream.h" #include "dap/session.h" // Assuming writer_ptr is a valid std::shared_ptr<dap::Writer> dap::ContentWriter content_writer(writer_ptr); if (content_writer.isOpen()) { bool success = content_writer.write("your content here"); content_writer.close(); }Send a DAP Event
mainTo broadcast an event, use the
sendoverload that acceptsTypeInfoand an event object. This method automatically assigns a sequence number and serializes the event body.Arguments:
typeinfo: Thedap::TypeInfodescribing the event.event: A pointer to the data object to be serialized as the event body.
session->send(eventTypeInfo, eventData);Start processing DAP messages
mainTo begin the asynchronous processing of incoming messages, call
startProcessingMessages(). This method spawns internal threads to:- Read messages from the connected reader.
- Dispatch payloads (requests, events, or responses) to their registered handlers.
You can optionally provide a
ClosedHandlercallback that will be executed when the reader is closed and the processing loop terminates.session->startProcessingMessages([]() { // Handle session closure });Use StringBuffer for in-memory I/O
mainThe
dap::StringBufferclass provides an in-memory buffer that implements both thedap::Readeranddap::Writerinterfaces. It allows you to write data into a buffer and subsequently read it back, making it useful for simulating I/O streams or handling data in memory.Key behaviors:
- Writing: You can write raw bytes or
std::stringobjects. Each write operation is tracked as a 'chunk'. - Reading: Reading consumes data from the front of the buffer. If you read more than the current chunk size, it moves to the next chunk.
- Lifecycle: Use
StringBuffer::create()to instantiate it. Onceclose()is called, the buffer is no longer writable and reading will return 0.
#include "dap/string_buffer.h" // Create a new buffer auto buffer = dap::StringBuffer::create(); // Write data std::string data = "Hello, DAP!"; buffer->write(data); // Read data back char read_buf[12]; size_t bytes_read = buffer->read(read_buf, 12); // Get the remaining content as a string std::string remaining = buffer->string();- Writing: You can write raw bytes or