protodef-cpp
Version:
C++ compiler for ProtoDef schemas to encode and decode packets
141 lines (117 loc) • 5.12 kB
Markdown
# protodef-cpp
[](http://npmjs.com/package/protodef-cpp)
[](https://github.com/extremeheat/protodef-cpp/actions?query=workflow%3A%22CI%22)
[](https://gitpod.io/#https://github.com/extremeheat/protodef-cpp)
[](https://discord.gg/GsEFRM8)
C++ compiler in Node.js for ProtoDef schemas, a lighter and more versatile alternative to Protocol Buffers or FlatBuffers that can support any binary format.
See the ProtoDef specification at https://github.com/ProtoDef-io/ProtoDef.
### Features
* Header-only : No dependencies other than the C or C++ standard libraries
* Arrays, enums, nested structures, optional fields, variable-length integers/strings, custom types, etc. See ProtoDef spec for more details
* Built-in support for dumping JSON for utility/debugging on top of binary encode/decode
## Install
```cmd
npm install protodef-cpp
```
## Usage
### via npx / command line
Feed the CLI tool a .json or .yaml file, and optionally specify a directory to write the generated files to.
Two header files will be written: a "streams.h", containing a binary stream implementation used by the encoder/decoder (if you don't already have one),
and a "protocol.h" header file (name will vary depending on your input name), containing the generated encoder/decoder code.
```go
$ npx protodef-cpp --help
protodef-cpp - v1.0.0
protodef-cpp - A C++ compiler for ProtoDef schemas
Options:
--input, -i Path to ProtoDef JSON/YAML file with protocol data
--lang, -l What language use compile to. Currently only C++. (default: cpp)
--output Output folder (default: ./)
--config, -t Path to JS file whose exports will be merged into the current options, which allows you to define custom types and variables.
--namespace What namespace to use, useful if you have multiple protocols. (default: proto)
```
If you have multiple protocols specified inside one JSON file, please use the programmatic API instead ([example](https://github.com/extremeheat/protodef-cpp/blob/master/examples/mcpc-protocol/main.js)).
### via code / programmatic API
```js
const protodefCpp = require('protodef-cpp')
protodefCpp.compile({
inputFile: 'protocol.json',
outputFolder: './',
lang: 'cpp',
namespace: 'proto',
})
```
### Example
protocol.yml:
```yaml
string: ["pstring", { countType: "varint" }]
packet_video_stream_connect:
server_uri: string
frame_send_frequency: lf32
action: u8 =>
1: none
2: close
resolution_x: li32
resolution_y: li32
```
<!-- <details>
<summary>Custom Types implementation in JavaScript</summary>
Inside types.js:
```js
module.exports = {
pstring: {
struct (args, name, makeCallingCode) {
return makeCallingCode('std::string', name)
},
read (args, [name, refName], makeCallingCode) {
if (args.count) return `if (!stream.readString(${refName}, ${args.count})) return false;`
const primitiveType = protodefTypeToCpp[args.countType]
return `
${primitiveType} ${name}_strlen; ${makeCallingCode(args.countType, `${name}_strlen`)};
if (!stream.readString(${refName}, ${name}_strlen)) return false;
`.trim()
},
write (args, [name, refName], makeCallingCode) {
if (args.count) return `WRITE_OR_BAIL(writeString, ${refName});`
return `
${makeCallingCode(args.countType, [refName, 'length()'])};
WRITE_OR_BAIL(writeString, ${refName});
`.trim()
},
size (args, [name, refName], makeCallingCode) {
if (args.count) return `len += ${args.count};`
return `
${makeCallingCode(args.countType, [refName, 'length()'])};
len += ${refName}.length();
`.trim()
}
}
}
```
</details> -->
Command line:
```cmd
npx protodef-cpp -i protocol.yml --lang cpp
```
Done! You should now have a "protocol.h" file in your current directory. Here's how you would encode a structure:
```cpp
#include "protocol.h"
int main () {
pdef::proto::packet_video_stream_connect packet {
.server_uri = "wss://example.com",
.frame_send_frequency = 60.0f,
.action = pdef::proto::packet_video_stream_connect::Action::None,
.resolution_x = 1920,
.resolution_y = 1080,
};
pdef::Stream stream(0);
pdef::proto::encode::packet_video_stream_connect(stream, packet);
stream.dumpToStdout();
return 0;
}
```
See [examples/](https://github.com/extremeheat/protodef-cpp/tree/master/examples) for more examples.
### Deviations from ProtoDef spec
protodef-cpp does not support root level switches, or $ variables. Some
preprocessing is done to inline root level switches, but for $ variables,
some complex use-cases may not work. Please update the schema to not use $
variables if you encounter any issues.