More cleanup
This commit is contained in:
60
style.md
60
style.md
@@ -21,7 +21,6 @@ ______________________________________________________________________
|
||||
|
||||
- **C++20** is the target standard
|
||||
- Use modern C++ features: RAII, move semantics, constexpr, concepts where appropriate
|
||||
- Prefer standard library containers and algorithms over custom implementations
|
||||
|
||||
### C Library Functions and Headers
|
||||
|
||||
@@ -65,16 +64,16 @@ signal(SIGTERM, handler);
|
||||
- Interfacing with APIs that require unsigned types
|
||||
- Where defined unsigned overflow behavior (wraparound) is intentional and desired
|
||||
- **Almost always auto** - let the compiler deduce types except when:
|
||||
- The type is not obvious from context (prefer explicit for clarity)
|
||||
- The type is not obvious from context and the exact type is important (prefer explicit for clarity)
|
||||
- Specific type requirements matter (numeric conversions, template parameters)
|
||||
- Interface contracts need explicit types (public APIs, function signatures)
|
||||
- **Prefer uninitialized memory to default initialization** when using before initializing would be an error
|
||||
- Valgrind will catch uninitialized memory usage bugs
|
||||
- Avoid hiding logic errors with unnecessary zero-initialization
|
||||
- Avoid hiding logic errors that Valgrind would have caught with unnecessary zero-initialization
|
||||
- Default initialization can mask bugs and hurt performance
|
||||
- **Floating point is for metrics only** - avoid `float`/`double` in core data structures and algorithms
|
||||
- Use for performance measurements, statistics, and monitoring data
|
||||
- Never use for counts, sizes, or business logic
|
||||
- Avoid branching on the values of floats
|
||||
|
||||
### Type Casting
|
||||
|
||||
@@ -106,7 +105,7 @@ auto addr = reinterpret_cast<uintptr_t>(ptr); // Pointer to integer conv
|
||||
- **Strive for 0% CPU usage when idle** - avoid polling, busy waiting, or unnecessary background activity
|
||||
- Use **inline functions** for performance-critical code (e.g., `allocate_raw`)
|
||||
- **String views** with `std::string_view` to minimize unnecessary copying
|
||||
- **Arena allocation** for efficient memory management (~1ns vs ~20-270ns for malloc)
|
||||
- **Arena allocation** for efficient memory management, and to group related lifetimes together for simplicity
|
||||
|
||||
### String Formatting
|
||||
|
||||
@@ -131,6 +130,8 @@ std::string_view response = format(arena,
|
||||
static_cast<int>(body.size()), body.data());
|
||||
```
|
||||
|
||||
- Offer APIs that let you avoid concatenating strings if possible - e.g. if the bytes are going to get written to a file descriptor you can skip concatenating and use scatter/gather writev-type calls.
|
||||
|
||||
### Complexity Control
|
||||
|
||||
- **Encapsulation is the main tool for controlling complexity**
|
||||
@@ -141,7 +142,7 @@ std::string_view response = format(arena,
|
||||
- Thread safety guarantees
|
||||
- Performance characteristics
|
||||
- Ownership and lifetime semantics
|
||||
- **Do not rely on undocumented interface properties** - if it's not in the header, don't depend on it
|
||||
- **Do not rely on undocumented properties of an interface** - if it's not in the header, don't depend on it
|
||||
|
||||
______________________________________________________________________
|
||||
|
||||
@@ -165,17 +166,16 @@ int32_t initial_block_size_;
|
||||
- **Public members first, private after** - puts the interface users care about at the top, implementation details below
|
||||
- **Full encapsulation still applies** - use `private:` sections to hide implementation details and maintain deep, capable structs
|
||||
- The struct keyword doesn't mean shallow design - it means interface-first organization for human readers
|
||||
- Omit the `public` keyword when inheriting from a struct. It's public by default. E.g. `struct A : B {};` instead of `struct A : public B {};`
|
||||
|
||||
```cpp
|
||||
struct Arena {
|
||||
struct MyClass {
|
||||
// Public interface first
|
||||
explicit Arena(int64_t initial_size = 1024);
|
||||
void* allocate_raw(int64_t size);
|
||||
void do_thing();
|
||||
|
||||
private:
|
||||
// Private members after
|
||||
int32_t initial_block_size_;
|
||||
Block* current_block_;
|
||||
int thing_count_;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -183,6 +183,7 @@ private:
|
||||
|
||||
- **PascalCase** for enum class names
|
||||
- **PascalCase** for enum values (not SCREAMING_SNAKE_CASE)
|
||||
- C-style enums are acceptable where implicit int conversion is desirable, like for bitflags
|
||||
|
||||
```cpp
|
||||
enum class Type {
|
||||
@@ -270,7 +271,7 @@ ______________________________________________________________________
|
||||
|
||||
- **Move-only semantics** for resource-owning types
|
||||
- **Explicit constructors** to prevent implicit conversions
|
||||
- **Delete copy operations** when inappropriate
|
||||
- **Delete copy operations** when copying is inappropriate or should be discouraged
|
||||
|
||||
```cpp
|
||||
struct Arena {
|
||||
@@ -313,7 +314,7 @@ Arena(Arena &&source) noexcept;
|
||||
|
||||
### Factory Patterns & Ownership
|
||||
|
||||
- **Static factory methods** for complex construction requiring shared ownership
|
||||
- **Static factory methods** for complex construction requirements like enforcing shared ownership
|
||||
- **Friend-based factories** for access control when constructor should be private
|
||||
- **Ownership guidelines:**
|
||||
- **unique_ptr** for exclusive ownership (most common case)
|
||||
@@ -329,7 +330,8 @@ auto connection = Connection::createForServer(addr, fd, connection_id, handler,
|
||||
|
||||
// Friend-based factory for access control
|
||||
struct Connection {
|
||||
void append_message(std::string_view message_data);
|
||||
WeakRef<MessageSender> get_weak_ref() const;
|
||||
|
||||
private:
|
||||
Connection(struct sockaddr_storage client_addr, int file_descriptor,
|
||||
int64_t connection_id, ConnectionHandler* request_handler,
|
||||
@@ -382,7 +384,7 @@ ______________________________________________________________________
|
||||
|
||||
### Ownership & Allocation
|
||||
|
||||
- **Arena allocators** for request-scoped memory with **STL allocator adapters** (see Performance Focus section for characteristics)
|
||||
- **Arena** for request-scoped memory with **STL allocator adapters**
|
||||
- **String views** pointing to arena-allocated memory to avoid unnecessary copying
|
||||
- **STL containers with arena allocators require default construction after arena reset** - `clear()` is not sufficient
|
||||
|
||||
@@ -425,7 +427,7 @@ ______________________________________________________________________
|
||||
- **Error codes are the API contract** - use enums for programmatic decisions
|
||||
- **Error messages are human-readable only** - never parse message strings
|
||||
- **Consistent error boundaries** - each component defines what it can/cannot recover from
|
||||
- **Interface precondition violations are undefined behavior** - acceptable to skip checks for performance in hot paths
|
||||
- **Interface precondition violations are undefined behavior** - it's acceptable to skip checks for performance in hot paths
|
||||
- **Error code types must be nodiscard** - mark error code enums with `[[nodiscard]]` to prevent silent failures
|
||||
|
||||
```cpp
|
||||
@@ -439,7 +441,7 @@ if (!memory) {
|
||||
}
|
||||
// ... use memory, eventually std::free(memory)
|
||||
|
||||
// Programming error - precondition violation (may be omitted for performance)
|
||||
// Programming error - precondition violation (gets compiled out in release builds)
|
||||
assert(ptr != nullptr && "Precondition violated: pointer must be non-null");
|
||||
```
|
||||
|
||||
@@ -546,7 +548,7 @@ T *realloc(T *existing_ptr, int32_t current_size, int32_t requested_size);
|
||||
|
||||
### Code Comments
|
||||
|
||||
- **Explain why, not what** - code should be self-documenting
|
||||
- **Explain why, not what** - *what* the code does should be clear without any comments
|
||||
- **Performance notes** for optimization decisions
|
||||
- **Thread safety** and ownership semantics
|
||||
|
||||
@@ -573,7 +575,7 @@ ______________________________________________________________________
|
||||
### Test Structure
|
||||
|
||||
- **Descriptive test names** explaining the scenario
|
||||
- **SUBCASE** for related test variations
|
||||
- **SUBCASE** for related test variations that share setup/teardown code
|
||||
- **Fresh instances** for each test to avoid state contamination
|
||||
|
||||
```cpp
|
||||
@@ -600,25 +602,14 @@ TEST_CASE("Arena basic allocation") {
|
||||
- **Prefer fakes to mocks** - use real implementations for internal components, fake external dependencies
|
||||
- **Always enable assertions in tests** - use `-UNDEBUG` pattern to ensure assertions are checked (see Build Integration section)
|
||||
|
||||
TODO make a new example here using APIs that exist
|
||||
|
||||
```cpp
|
||||
// Good: Testing through public API
|
||||
TEST_CASE("Server accepts connections") {
|
||||
auto config = Config::defaultConfig();
|
||||
auto handler = std::make_unique<TestHandler>();
|
||||
auto server = Server::create(config, std::move(handler));
|
||||
|
||||
// Test observable behavior - server can accept connections
|
||||
auto result = connectToServer(server->getPort());
|
||||
CHECK(result.connected);
|
||||
}
|
||||
|
||||
// Avoid: Testing internal implementation details
|
||||
// TEST_CASE("Server creates epoll instance") { /* implementation detail */ }
|
||||
```
|
||||
|
||||
### What NOT to Test
|
||||
|
||||
**Avoid testing language features and plumbing:**
|
||||
**Avoid testing language features:**
|
||||
|
||||
- Don't test that virtual functions dispatch correctly
|
||||
- Don't test that standard library types work (unique_ptr, containers, etc.)
|
||||
@@ -716,8 +707,9 @@ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
```cmake
|
||||
# Test target with assertions always enabled
|
||||
add_executable(test_example tests/test_example.cpp src/example.cpp)
|
||||
target_link_libraries(test_example doctest::doctest)
|
||||
target_link_libraries(test_example doctest_impl)
|
||||
target_compile_options(test_example PRIVATE -UNDEBUG) # Always enable assertions
|
||||
add_test(NAME test_example COMMAND test_example)
|
||||
|
||||
# Production target follows build type
|
||||
add_executable(example src/example.cpp src/main.cpp)
|
||||
|
||||
Reference in New Issue
Block a user