Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions CROSSPLATFORM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# Cross-Platform Portability Changes for Astra

This document tracks all code changes made to improve cross-platform compatibility (especially for GCC/Clang/MSVC) in Astra.

## 1. Macro Portability Fixes
- All uses of `HAS_BUILTIN` and `ASTRA_HAS_BUILTIN` in `include/Astra/Platform/Simd.hpp` were updated to:
- Always wrap macro checks in `defined(...)` to avoid preprocessor errors on strict compilers.
- The original lines are commented above each change for traceability.

## 2. Defaulted Brace-Initializer Fixes
- All function parameters of the form `const Type& param = {}` were changed to `Type param = {}` in the following files:
- `include/Astra/Registry/Registry.hpp`
- `explicit Registry(Config config = {})` (was `const Config& config = {}`)
- `Registry(std::shared_ptr<ComponentRegistry> componentRegistry, Config config = {})` (was `const Config& config = {}`)
- `explicit Registry(const Registry& other, Config config = {})` (was `const Config& config = {}`)
- `Defragment(DefragmentationOptions options = {})` (was `const DefragmentationOptions& options = {}`)
- `Save(const std::filesystem::path& path, SaveConfig config = SaveConfig{}) const` (was `const SaveConfig& config = SaveConfig{}`)
- `Save(SaveConfig config = SaveConfig{}) const` (was `const SaveConfig& config = SaveConfig{}`)
- `include/Astra/Archetype/ArchetypeChunkPool.hpp`
- `explicit ArchetypeChunkPool(Config config = {})` (was `const Config& config = {}`)
- `include/Astra/Archetype/ArchetypeManager.hpp`
- `explicit ArchetypeManager(ArchetypeChunkPool::Config poolConfig = {})` (was `const ArchetypeChunkPool::Config& poolConfig = {}`)
- `ArchetypeManager(std::shared_ptr<ComponentRegistry> registry, ArchetypeChunkPool::Config poolConfig = {})` (was `const ArchetypeChunkPool::Config& poolConfig = {}`)
- `CleanupEmptyArchetypes(CleanupOptions options = {})` (was `const CleanupOptions& options = {}`)

## 3. Configuration Struct Default Constructor Fixes
The following Config structs needed explicit default constructors for GCC/Clang brace initialization compatibility:

## CROSS-PLATFORM COMPATIBILITY STATUS: SUCCESS

**BUILD STATUS**: SUCCESSFUL
**COMPILER**: GCC 15 on Linux
**DATE**: Successfully transitioned from MSVC-only to cross-platform build

### COMPLETED FIXES:
- `Registry::Config` - Fixed constructor ambiguity with overloaded constructors approach
- `Registry::DefragmentationOptions` - Fixed with overloaded method approach
- `Registry::SaveConfig` - Fixed with overloaded method approach
- `ArchetypeChunkPool::Config` - Fixed with constructor delegation approach
- `ArchetypeManager::CleanupOptions` - Fixed with overloaded method approach
- `EntityManager::Config` - **FIXED CONSTRUCTOR AMBIGUITY**: Removed conflicting `Config() = default;`, keeping only parameterized constructor
- `EntityTable::Config` - **FIXED CONSTRUCTOR AMBIGUITY**: Removed conflicting `Config() = default;`, keeping only parameterized constructor
- `Query.hpp Template Issues` - **MAJOR FIX**: Moved `GetRequiredImpl` template specializations to namespace scope (GCC requires explicit template specializations at namespace level, not class level)
- `ArchetypeManager Constructor` - Fixed missing Config parameter in make_shared calls

### CRITICAL ISSUES RESOLVED:
1. **Template Specialization Scope**: Fixed Query.hpp `GetRequiredImpl` template hierarchy - moved explicit specializations from class scope to namespace scope for GCC compliance
2. **Constructor Ambiguity**: Resolved `Config() = default` vs `Config(param = default)` conflicts by using single parameterized constructors
3. **Default Member Initializer Issues**: Fixed GCC's strict requirements for aggregate initialization by using constructor delegation and overloaded methods
4. **ArchetypeManager Constructor**: Fixed missing Config parameters in `make_shared<ArchetypeManager>` calls

### BUILD EVIDENCE:
- **Before**: 0 .cpp files compiling, basic syntax errors
- **After**: ALL .cpp files compile successfully, only minor warnings remain
- **Error Evolution**: Progressed from basic syntax → sophisticated template instantiation → successful compilation

## 4. Rationale
- These changes are required because GCC and Clang do not allow defaulting a const reference parameter to a brace-initializer ({}), which is allowed by MSVC.
- Passing by value is safe and efficient for small config structs, and enables default construction with `{}` on all platforms.
- GCC requires explicit default constructors for structs used in brace initialization (e.g., `Config{}`), while MSVC is more permissive.

## Build Status
**CURRENT**: Build failing with critical Config ambiguity and template issues
**COMMAND**: `cd /home/soleo/Desktop/Astra-2.0.0 && make -j$(nproc)`

## Next Steps
1. Fix Config constructor ambiguity issues
2. Restructure Query.hpp template hierarchy
3. Complete systematic Config struct audit
4. Verify successful build

## 5. Why These Errors Are More Sophisticated

### Root Cause Analysis
The current build errors represent **advanced C++ standard compliance issues** rather than basic syntax problems. These are **pre-existing design flaws** that were **masked by MSVC's permissiveness** but correctly rejected by GCC/Clang's stricter compliance.

**Key Insight**: Our hotfixes didn't create these problems - they **exposed** them by allowing compilation to proceed deeper into the codebase.

### Error Sophistication Levels:

#### **BASIC ERRORS (Fixed in earlier phases):**
- Header parsing failures
- Macro definition problems
- Missing include paths
- Template syntax issues

#### **SOPHISTICATED ERRORS (Current phase):**
- **Constructor Ambiguity**: Subtle language feature where having both `Config()` and `Config(T = default)` creates ambiguity for `Config{}` calls
- **Template Instantiation**: Deep template metaprogramming where GCC's stricter template resolution rules expose issues
- **Member Initializer Ordering**: Semantic analysis errors about when default member initializers are considered "complete"
- **Template Specialization Scope**: Complex template hierarchy issues with nested specializations

### Specific Examples:

#### **Constructor Ambiguity Pattern (DESIGN FLAW)**
```cpp
// PROBLEMATIC: Conflicting constructors
struct Config {
int value = 42;
Config() = default; // Uses default member initializer (value = 42)
Config(int v = 42) : value(v) {} // Calculates value, ignores default member initializer
};
// Compiler can't decide: Config{} - which constructor?
```

**Root Cause**: Inconsistent initialization - one constructor uses default member initializers, the other overrides them.

**Fix Strategy**: Remove conflicting default constructor, make parameterized constructor handle defaults:
```cpp
// FIXED: Single consistent constructor
struct Config {
int value = 42; // Default member initializer for non-calculated members
Config(int v = 42) : value(v) {} // Handles both default and custom cases
};
```

#### **Default Member Initializer Ordering (GCC STRICTNESS)**
```cpp
// PROBLEMATIC: Constructor defined in class body with Config{} default parameter
class Pool {
struct Config { int size = 1024; Config() = default; };
public:
explicit Pool(Config config = Config{}) {} // ERROR: Config{} not "complete" yet in GCC
};
```

**Root Cause**: GCC has stricter rules about when default member initializers are considered "complete" for brace initialization in default parameters.

**Fix Strategy**: Use `= {}` instead of `= Config{}` to rely on aggregate initialization:
```cpp
// FIXED: Use aggregate initialization
explicit Pool(Config config = {}) {} // Works with GCC's stricter timing
```

### Why MSVC vs GCC Differs:

1. **MSVC Philosophy**: More permissive, tries to "make it work" even with ambiguous cases
2. **GCC Philosophy**: Stricter C++ standard compliance, rejects ambiguous constructs
3. **Template Processing**: GCC has more rigorous template instantiation timing
4. **Member Initialization**: GCC enforces stricter ordering of when member initializers are "complete"

### Implications for Future Code:

#### **Design Guidelines to Prevent These Issues:**
1. **Avoid Constructor Ambiguity**: Don't mix `= default` constructors with parameterized constructors that have defaults
2. **Consistent Initialization**: If using default member initializers, let them handle defaults rather than overriding in constructors
3. **Template Scope Clarity**: Keep template specializations at appropriate namespace scope levels
4. **Aggregate Initialization**: Prefer `= {}` over `= StructName{}` in default parameters for better cross-platform compatibility

#### **Cross-Platform Testing Strategy:**
1. **Primary Development**: Use stricter compiler (GCC/Clang) as primary to catch issues early
2. **Secondary Validation**: Test on MSVC to ensure compatibility isn't broken
3. **Template Heavy Code**: Extra scrutiny on template metaprogramming for scope issues
4. **Config Patterns**: Audit all Config-style structs for constructor consistency


## 4. Struct Definition Ordering for Default Parameters (GCC/Clang)

- **Issue:** GCC/Clang require that any struct/class used as a default parameter (e.g., `Config config = Config{}`) must have its full definition (with all default member initializers) visible before the function declaration/definition. If the struct is only forward-declared or defined after the function, you will get errors like:
- `error: default member initializer for '...' required before the end of its enclosing class`

- **Hotfix:** Move the full struct definition (with all default member initializers) above any function that uses it as a default parameter. Do not forward-declare and define later.

- **Example Fix:**
- Move the definition of `SaveConfig` above the `Save` function in `Registry.hpp`.
- Move the definition of `DefragmentationOptions` above the `Defragment` function in `Registry.hpp`.

- **Original code (problematic order):**
```cpp
// ...
Result<void, SerializationError> Save(const std::filesystem::path& path, SaveConfig config = SaveConfig{}) const;
// ...
struct SaveConfig {
CompressionMode compressionMode = CompressionMode::LZ4;
Compression::CompressionLevel compressionLevel = Compression::CompressionLevel::Fast;
size_t compressionThreshold = 1024;
};
```

- **Hotfix (correct order):**
```cpp
struct SaveConfig {
CompressionMode compressionMode = CompressionMode::LZ4;
Compression::CompressionLevel compressionLevel = Compression::CompressionLevel::Fast;
size_t compressionThreshold = 1024;
};
// ...
Result<void, SerializationError> Save(const std::filesystem::path& path, SaveConfig config = SaveConfig{}) const;
```

- **Rationale:** This ensures the compiler knows how to default-construct the struct at the point of use.

---

## 5. Next Steps
- Continue to address other portability issues (requires expressions, template metaprogramming, etc.) as they are discovered in the build process.
Loading