Improve style.md
This commit is contained in:
69
style.md
69
style.md
@@ -26,7 +26,7 @@ This document describes the C++ coding style used in the WeaselDB project. These
|
||||
- **Always use std:: prefixed versions** of C library functions for consistency and clarity
|
||||
- **Use C++ style headers** (`<cstring>`, `<cstdlib>`, etc.) instead of C style headers (`<string.h>`, `<stdlib.h>`, etc.)
|
||||
- This applies to all standard libc functions: `std::abort()`, `std::fprintf()`, `std::free()`, `std::memcpy()`, `std::strlen()`, `std::strncpy()`, `std::memset()`, `std::signal()`, etc.
|
||||
- Exception: Functions with no std:: equivalent (e.g., `perror()`, `gai_strerror()`) and system-specific headers (e.g., `<unistd.h>`, `<fcntl.h>`)
|
||||
- **Exception:** Functions with no std:: equivalent (e.g., `perror()`, `gai_strerror()`) and system-specific headers (e.g., `<unistd.h>`, `<fcntl.h>`)
|
||||
```cpp
|
||||
// Preferred - C++ style
|
||||
#include <cstring>
|
||||
@@ -89,7 +89,8 @@ auto ptr = static_cast<int*>(malloc(sizeof(int))); // Explicit conversion
|
||||
auto base = static_cast<BaseClass*>(derived_ptr); // Safe upcast
|
||||
auto derived = dynamic_cast<DerivedClass*>(base_ptr); // Runtime type checking
|
||||
auto mutable_ptr = const_cast<int*>(const_ptr); // Remove const (rare)
|
||||
auto addr = reinterpret_cast<uintptr_t>(ptr); // Low-level operations (very rare, delicate)
|
||||
// reinterpret_cast can be appropriate for low-level operations (very rare)
|
||||
auto addr = reinterpret_cast<uintptr_t>(ptr); // Pointer to integer conversion
|
||||
```
|
||||
|
||||
### Performance Focus
|
||||
@@ -99,7 +100,7 @@ auto addr = reinterpret_cast<uintptr_t>(ptr); // Low-level operations (v
|
||||
- **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`)
|
||||
- **Zero-copy operations** with `std::string_view` over string copying
|
||||
- **Arena allocation** for efficient memory management (see Memory Management section for details)
|
||||
- **Arena allocation** for efficient memory management (~1ns vs ~20-270ns for malloc)
|
||||
|
||||
### Complexity Control
|
||||
- **Encapsulation is the main tool for controlling complexity**
|
||||
@@ -124,11 +125,11 @@ void add_block(int64_t size);
|
||||
int32_t initial_block_size_;
|
||||
```
|
||||
|
||||
### Structs
|
||||
- **PascalCase** for struct names
|
||||
- **Always use struct** - eliminates debates about complexity and maintains consistency
|
||||
### Classes and Structs
|
||||
- **PascalCase** for class/struct names
|
||||
- **Always use struct keyword** - eliminates debates about complexity and maintains consistency
|
||||
- **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 classes
|
||||
- **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
|
||||
```cpp
|
||||
struct ArenaAllocator {
|
||||
@@ -220,8 +221,8 @@ std::unique_ptr<Parser> parser;
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Struct Design
|
||||
- **Move-only semantics** for resource-owning structs
|
||||
### Class Design
|
||||
- **Move-only semantics** for resource-owning types
|
||||
- **Explicit constructors** to prevent implicit conversions
|
||||
- **Delete copy operations** when inappropriate
|
||||
```cpp
|
||||
@@ -229,12 +230,12 @@ struct ArenaAllocator {
|
||||
explicit ArenaAllocator(int64_t initial_size = 1024);
|
||||
|
||||
// Copy construction is not allowed
|
||||
ArenaAllocator(const ArenaAllocator &) = delete;
|
||||
ArenaAllocator &operator=(const ArenaAllocator &) = delete;
|
||||
ArenaAllocator(const ArenaAllocator &source) = delete;
|
||||
ArenaAllocator &operator=(const ArenaAllocator &source) = delete;
|
||||
|
||||
// Move semantics
|
||||
ArenaAllocator(ArenaAllocator &&other) noexcept;
|
||||
ArenaAllocator &operator=(ArenaAllocator &&other) noexcept;
|
||||
ArenaAllocator(ArenaAllocator &&source) noexcept;
|
||||
ArenaAllocator &operator=(ArenaAllocator &&source) noexcept;
|
||||
|
||||
private:
|
||||
int32_t initial_block_size_;
|
||||
@@ -251,9 +252,9 @@ private:
|
||||
- **noexcept specification** for move operations and non-throwing functions
|
||||
```cpp
|
||||
std::span<const Operation> operations() const { return operations_; }
|
||||
void process_data(std::string_view data); // ≤ 16 bytes, pass by value
|
||||
void process_request(const CommitRequest& req); // > 16 bytes, pass by reference
|
||||
ArenaAllocator(ArenaAllocator &&other) noexcept;
|
||||
void process_data(std::string_view request_data); // ≤ 16 bytes, pass by value
|
||||
void process_request(const CommitRequest& commit_request); // > 16 bytes, pass by reference
|
||||
ArenaAllocator(ArenaAllocator &&source) noexcept;
|
||||
```
|
||||
|
||||
### Template Usage
|
||||
@@ -273,13 +274,15 @@ ArenaAllocator(ArenaAllocator &&other) noexcept;
|
||||
auto server = Server::create(config, handler); // Returns shared_ptr
|
||||
|
||||
// Exclusive ownership - single owner, transfer via move
|
||||
auto connection = Connection::createForServer(...); // Returns unique_ptr
|
||||
auto connection = Connection::createForServer(addr, fd, connection_id, handler, server_ref);
|
||||
|
||||
// Friend-based factory for access control
|
||||
struct Connection {
|
||||
void appendMessage(std::string_view data);
|
||||
void appendMessage(std::string_view message_data);
|
||||
private:
|
||||
Connection(/* args */); // Private constructor
|
||||
Connection(struct sockaddr_storage client_addr, int file_descriptor,
|
||||
int64_t connection_id, ConnectionHandler* request_handler,
|
||||
std::weak_ptr<Server> server_ref);
|
||||
friend struct Server; // Only Server can construct
|
||||
};
|
||||
```
|
||||
@@ -302,15 +305,15 @@ for (auto &precondition : preconditions_) {
|
||||
## Memory Management
|
||||
|
||||
### Ownership & Allocation
|
||||
- **Arena allocators** for request-scoped memory with **STL allocator adapters** (provides ~1ns allocation vs ~20-270ns for malloc)
|
||||
- **Arena allocators** for request-scoped memory with **STL allocator adapters** (see Performance Focus section for characteristics)
|
||||
- **String views** pointing to arena-allocated memory for zero-copy operations
|
||||
- **STL containers with arena allocators require default construction after arena reset** - `clear()` is not sufficient
|
||||
```cpp
|
||||
// STL containers with arena allocators - correct reset pattern
|
||||
std::vector<Operation, ArenaStlAllocator<Operation>> operations(arena_alloc);
|
||||
std::vector<Operation, ArenaStlAllocator<Operation>> operations(arena_allocator);
|
||||
// ... use container ...
|
||||
operations = {}; // Default construct - clear() won't work correctly
|
||||
arena.reset(); // Reset arena memory
|
||||
arena_allocator.reset(); // Reset arena memory
|
||||
```
|
||||
|
||||
### Resource Management
|
||||
@@ -346,12 +349,12 @@ arena.reset(); // Reset arena memory
|
||||
enum class ParseResult { Success, InvalidJson, MissingField };
|
||||
|
||||
// System failure - abort immediately
|
||||
void* memory = malloc(size);
|
||||
void* memory = std::malloc(size);
|
||||
if (!memory) {
|
||||
std::fprintf(stderr, "ArenaAllocator: Memory allocation failed\n");
|
||||
std::abort();
|
||||
}
|
||||
// ... use memory, eventually free it
|
||||
// ... use memory, eventually std::free(memory)
|
||||
|
||||
// Programming error - precondition violation (may be omitted for performance)
|
||||
assert(ptr != nullptr && "Precondition violated: pointer must be non-null");
|
||||
@@ -396,9 +399,9 @@ do {
|
||||
} while (fd == -1 && errno == EINTR);
|
||||
|
||||
if (fd == -1) {
|
||||
// Handle other errors
|
||||
// Handle other errors (perror has no std:: equivalent)
|
||||
perror("accept");
|
||||
abort();
|
||||
std::abort();
|
||||
}
|
||||
```
|
||||
|
||||
@@ -408,9 +411,9 @@ The `close()` system call is a special case on Linux. According to `man 2 close`
|
||||
|
||||
```cpp
|
||||
// Correct: Do not retry close() on EINTR
|
||||
int e = close(fd);
|
||||
if (e == -1 && errno != EINTR) {
|
||||
// Handle non-EINTR errors only
|
||||
int result = close(fd);
|
||||
if (result == -1 && errno != EINTR) {
|
||||
// Handle non-EINTR errors only (perror has no std:: equivalent)
|
||||
perror("close");
|
||||
std::abort();
|
||||
}
|
||||
@@ -444,14 +447,14 @@ When in doubt, consult the `man` page for the specific system call to see if it
|
||||
```cpp
|
||||
/**
|
||||
* @brief Type-safe version of realloc_raw for arrays of type T.
|
||||
* @param ptr Pointer to the existing allocation
|
||||
* @param old_size Size in number of T objects
|
||||
* @param new_size Desired new size in number of T objects
|
||||
* @param existing_ptr Pointer to the existing allocation
|
||||
* @param current_size Size in number of T objects
|
||||
* @param requested_size Desired new size in number of T objects
|
||||
* @return Pointer to reallocated memory
|
||||
* @note Prints error to stderr and calls std::abort() if allocation fails
|
||||
*/
|
||||
template <typename T>
|
||||
T *realloc(T *ptr, int32_t old_size, int32_t new_size);
|
||||
T *realloc(T *existing_ptr, int32_t current_size, int32_t requested_size);
|
||||
```
|
||||
|
||||
### Code Comments
|
||||
|
||||
Reference in New Issue
Block a user