Inside the companion
Modern C++ errors
Follow one pixel through validation, std::expected, and a complete-or-unchanged update.
Project files 6
Validate before publishing
Validate and encode channels, attach an error index, and commit a complete pixel.
#include "pixel/pixel.hpp"
#include <cmath>
namespace pixel {
std::string_view describe(ChannelError error) noexcept {
switch (error) {
case ChannelError::non_finite: return "channel must be finite";
case ChannelError::outside_unit_interval: return "channel must be in [0,1]";
}
return "unknown channel error";
}
std::expected<std::uint8_t, ChannelError> encode_channel(double linear) {
if (!std::isfinite(linear)) return std::unexpected(ChannelError::non_finite);
if (linear < 0.0 || linear > 1.0)
return std::unexpected(ChannelError::outside_unit_interval);
// Validation establishes the finite [0,255] conversion domain.
return static_cast<std::uint8_t>(std::lround(std::sqrt(linear) * 255.0));
}
std::expected<Pixel, PixelError> prepare_pixel(std::array<double, 3> linear) {
std::array<std::uint8_t, 3> encoded{};
for (std::size_t channel = 0; channel < linear.size(); ++channel) {
const auto result = encode_channel(linear[channel]);
if (!result) return std::unexpected(PixelError{channel, result.error()});
encoded[channel] = *result;
}
return Pixel{encoded[0], encoded[1], encoded[2]};
}
std::expected<void, PixelError> replace_pixel(
Pixel& current, std::array<double, 3> linear) {
const auto candidate = prepare_pixel(linear);
if (!candidate) return std::unexpected(candidate.error());
current = *candidate; // Trivial assignment is the commit; it cannot throw.
return {};
}
}
This is the file included with the course. Copy and download give you its complete source.
Taking your work offline? Download all course companions
