Result and TResult
Result and TResult provide a modern error handling mechanism that allows functions to return either a success value or detailed error information without using exceptions. This makes error handling explicit and helps prevent errors from being silently ignored.
Basic usage
Result (no return value)
Use Result for operations that don't return a value but can fail:
Result SaveConfiguration(const String& filename)
{
if(filename.empty())
return Result::Fail("Filename cannot be empty");
if(!FileSystem::Exists(filename.GetDirectory()))
return Result::Fail("Directory does not exist", ResultStatus::FailedNotFound);
// Perform save operation
bool saveSucceeded = PerformSave(filename);
if(!saveSucceeded)
return Result::Fail("Failed to write file", ResultStatus::FailedInternalError);
return Result::Success();
}
// Usage
Result result = SaveConfiguration("config.json");
if(result.IsSuccessful())
{
B3D_LOG(Info, LogGeneric, "Configuration saved successfully");
}
else
{
B3D_LOG(Error, LogGeneric, "Save failed: {0}", result.GetFullErrorMessage());
}
TResult (with return value)
Use TResult when an operation returns a value on success:
TResult<HTexture> LoadTexture(const Path& texturePath)
{
if(!FileSystem::Exists(texturePath))
return TResult<HTexture>::Fail("Texture file not found", ResultStatus::FailedNotFound);
HTexture texture = GetImporter().Import<Texture>(texturePath);
if(!texture)
return TResult<HTexture>::Fail("Failed to import texture");
return TResult<HTexture>::Success(texture);
}
// Usage
TResult<HTexture> result = LoadTexture("Textures/Player.png");
if(result.IsSuccessful())
{
HTexture texture = result.Output;
// Use the texture
}
else
{
B3D_LOG(Error, LogGeneric, "Failed to load texture: {0}", result.GetFullErrorMessage());
}
Result status codes
ResultStatus provides standard status codes for different failure scenarios:
- Succeeded - Operation completed successfully
- Failed - General failure
- FailedAlreadyExists - Resource or entity already exists
- FailedInvalidInput - Invalid input parameters
- FailedInternalError - Internal error occurred
- FailedNotFound - Resource or entity not found
- FailedReadOnly - Attempted to modify read-only resource
Error messages
Results can have two error messages:
- ErrorMessage - Primary error message (const char*, lightweight)
- AdditionalErrorMessage - Secondary message for detailed context (String)
TResult<HMesh> LoadMesh(const Path& meshPath)
{
if(!FileSystem::Exists(meshPath))
{
String details = "Searched in: " + meshPath.GetDirectory().ToString();
return TResult<HMesh>::Fail("Mesh file not found",
ResultStatus::FailedNotFound, details);
}
HMesh mesh = GetImporter().Import<Mesh>(meshPath);
return TResult<HMesh>::Success(mesh);
}
// Usage
TResult<HMesh> result = LoadMesh("Models/Character.fbx");
if(!result.IsSuccessful())
{
// Get combined error message
String fullError = result.GetFullErrorMessage();
B3D_LOG(Error, LogGeneric, "Mesh loading failed: {0}", fullError);
}
Chaining results
You can chain results by inheriting error status from child operations:
Result ValidateConfiguration(const ConfigData& config)
{
if(config.Name.empty())
return Result::Fail("Configuration name is required", ResultStatus::FailedInvalidInput);
return Result::Success();
}
Result SaveConfiguration(const ConfigData& config)
{
// Validate first
Result validationResult = ValidateConfiguration(config);
if(!validationResult.IsSuccessful())
{
// Chain the error, adding context
return Result::Fail("Configuration validation failed",
std::move(validationResult));
}
// Proceed with save
bool saved = PerformSave(config);
if(!saved)
return Result::Fail("File write failed");
return Result::Success();
}
// The error messages are chained together
Result result = SaveConfiguration(config);
if(!result.IsSuccessful())
{
// Will show: "Configuration validation failed: Configuration name is required"
B3D_LOG(Error, LogGeneric, "{0}", result.GetFullErrorMessage());
}