# Code Style & Conventions ## Overview This document describes the coding conventions and patterns observed throughout the libnbt++ codebase. These are not arbitrary style choices — they reflect deliberate design decisions for a C++11 library focused on correctness, interoperability, and clean ownership semantics. --- ## Namespaces ### Primary Namespaces | Namespace | Purpose | Location | |-----------|---------|----------| | `nbt` | All public API types: tags, value, visitors | `include/` | | `nbt::detail` | Internal implementation details | `include/crtp_tag.h`, `include/tag_primitive.h` | | `nbt::io` | Binary serialization (stream_reader, stream_writer) | `include/io/` | | `nbt::text` | Text formatting (json_formatter) | `include/text/` | | `endian` | Byte-order conversion functions | `include/endian_str.h` | | `zlib` | Compression stream wrappers | `include/io/izlibstream.h`, `include/io/ozlibstream.h` | ### Namespace Usage - All user-facing code is in the `nbt` namespace - Internal helpers like `crtp_tag` and `make_unique` are in `nbt::detail` (not `nbt`) - The `endian` and `zlib` namespaces are top-level, **not** nested under `nbt` - No `using namespace` directives appear in headers --- ## Export Macro ```cpp #ifdef NBT_SHARED #ifdef NBT_BUILD #define NBT_EXPORT __declspec(dllexport) // When building the shared lib #else #define NBT_EXPORT __declspec(dllimport) // When consuming the shared lib #endif #else #define NBT_EXPORT // Static build: empty #endif ``` `NBT_EXPORT` is applied to all public classes and free functions: ```cpp class NBT_EXPORT tag { ... }; class NBT_EXPORT tag_list final : public detail::crtp_tag { ... }; NBT_EXPORT bool operator==(const tag_compound& lhs, const tag_compound& rhs); ``` Classes in `nbt::detail` (like `crtp_tag`) are **not** exported. --- ## Class Design Patterns ### final Classes All concrete tag classes are `final`: ```cpp class tag_list final : public detail::crtp_tag { ... }; class tag_compound final : public detail::crtp_tag { ... }; class tag_string final : public detail::crtp_tag { ... }; ``` This prevents further inheritance and enables compiler devirtualization optimizations. ### CRTP Intermediate The Curiously Recurring Template Pattern eliminates boilerplate: ```cpp template class crtp_tag : public tag { tag_type get_type() const noexcept override { return Sub::type; } std::unique_ptr clone() const& override { return make_unique(/*copy*/); } std::unique_ptr move_clone() && override { return make_unique(std::move(/*this*/)); } void accept(nbt_visitor& visitor) const override { visitor.visit(const_cast(...)); } // ... }; ``` Each concrete class inherits from `crtp_tag` and gets all 6 virtual method implementations for free. ### Static Type Constants Each tag class exposes its type as a `static constexpr`: ```cpp class tag_int final : public detail::crtp_tag { public: static constexpr tag_type type = tag_type::Int; // ... }; ``` Used for compile-time type checks and template metaprogramming. --- ## Ownership Conventions ### Unique Pointer Everywhere All tag ownership uses `std::unique_ptr`: ```cpp std::unique_ptr tag::create(tag_type type); std::unique_ptr tag::clone() const&; std::unique_ptr stream_reader::read_payload(tag_type type); ``` ### Custom make_unique Since `std::make_unique` is C++14, the library provides its own in `nbt::detail`: ```cpp namespace detail { template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } } ``` Used throughout source files instead of raw `new`: ```cpp tags.emplace_back(make_unique(std::forward(args)...)); ``` ### value as Type-Erased Wrapper The `value` class wraps `std::unique_ptr` to provide implicit conversions and operator overloading that `unique_ptr` cannot support: ```cpp class value { std::unique_ptr tag_; public: value& operator=(int32_t val); // Assigns to contained tag operator int32_t() const; // Reads from contained tag value& operator[](const std::string& key); // Delegates to tag_compound // ... }; ``` --- ## C++11 Features Used | Feature | Usage | |---------|-------| | `std::unique_ptr` | All tag ownership | | Move semantics | `value(value&&)`, `tag_list::push_back(value_initializer&&)` | | `override` | All virtual method overrides | | `final` | All concrete tag classes | | `constexpr` | Static type constants, writer limits | | `noexcept` | `get_type()`, visitor destructors | | `std::initializer_list` | Compound and list construction | | Range-based for | Internal iteration in compounds, lists | | `auto` | Type deduction in local variables | | `static_assert` | Endian implementation checks | | `enum class` | `tag_type`, `endian::endian` | | Variadic templates | `emplace_back(Args&&...)`, `make_unique` | The library does **not** use C++14 or later features, maintaining broad compiler compatibility. --- ## Include Structure ### Public Headers (for Library Users) ``` include/ tag.h // tag base, tag_type enum, create(), as() crtp_tag.h // CRTP intermediate (detail) nbt_tags.h // Master include — includes all tag headers tag_primitive.h // tag_byte, tag_short, tag_int, tag_long, tag_float, tag_double tag_string.h // tag_string tag_array.h // tag_byte_array, tag_int_array, tag_long_array tag_list.h // tag_list tag_compound.h // tag_compound value.h // value wrapper value_initializer.h // value_initializer for implicit construction nbt_visitor.h // nbt_visitor, const_nbt_visitor endian_str.h // endian read/write functions io/ stream_reader.h // stream_reader, read_compound(), read_tag() stream_writer.h // stream_writer, write_tag() izlibstream.h // izlibstream (decompression) ozlibstream.h // ozlibstream (compression) zlib_streambuf.h // Base streambuf for zlib text/ json_formatter.h // json_formatter ``` ### Master Include `nbt_tags.h` includes all tag types for convenience: ```cpp // nbt_tags.h #include "tag.h" #include "tag_primitive.h" #include "tag_string.h" #include "tag_array.h" #include "tag_list.h" #include "tag_compound.h" #include "value.h" #include "value_initializer.h" ``` Users can include individual headers for faster compilation or `nbt_tags.h` for convenience. --- ## Error Handling Style ### Exceptions for Programmer Errors - `std::invalid_argument`: Type mismatches in lists, null values - `std::out_of_range`: Invalid indices in lists, missing keys in compounds - `std::logic_error`: Inconsistent internal state ### Exceptions for I/O Errors - `io::input_error` (extends `std::runtime_error`): All parse/read failures - `std::runtime_error`: Write stream failures - `std::length_error`: Exceeding NBT format limits - `zlib::zlib_error` (extends `std::runtime_error`): Compression/decompression failures ### Stream State Reader/writer methods check `is` / `os` state after I/O operations and throw on failure. Write methods set failbit before throwing to maintain consistent stream state. --- ## Naming Conventions | Element | Convention | Examples | |---------|-----------|----------| | Classes | `snake_case` | `tag_compound`, `stream_reader`, `tag_list` | | Methods | `snake_case` | `get_type()`, `read_payload()`, `el_type()` | | Member variables | `snake_case` with trailing `_` | `el_type_`, `tag_`, `is`, `os` | | Template parameters | `PascalCase` | `Sub`, `T`, `Args` | | Enum values | `PascalCase` | `tag_type::Byte_Array`, `tag_type::Long_Array` | | Namespaces | `snake_case` | `nbt`, `nbt::io`, `nbt::detail` | | Macros | `UPPER_SNAKE_CASE` | `NBT_EXPORT`, `NBT_HAVE_ZLIB`, `NBT_SHARED` | | Constants | `snake_case` | `max_string_len`, `max_array_len`, `MAX_DEPTH` | --- ## Template Patterns ### Explicit Instantiation Template classes like `tag_primitive` and `tag_array` use extern template declarations in headers and explicit instantiation in source files: ```cpp // In tag_primitive.h extern template class tag_primitive; extern template class tag_primitive; extern template class tag_primitive; extern template class tag_primitive; extern template class tag_primitive; extern template class tag_primitive; // In tag.cpp template class tag_primitive; template class tag_primitive; // ... ``` This prevents duplicate template instantiation across translation units, reducing compile time and binary size. ### Type Aliases ```cpp typedef tag_primitive tag_byte; typedef tag_primitive tag_short; typedef tag_primitive tag_int; typedef tag_primitive tag_long; typedef tag_primitive tag_float; typedef tag_primitive tag_double; typedef tag_array tag_byte_array; typedef tag_array tag_int_array; typedef tag_array tag_long_array; ``` Uses `typedef` rather than `using` — a C++11 compatibility choice, though both are equivalent.