gists/cpp/compile-time-settings-check.md

2.5 KiB
Raw Permalink Blame History

It is sometimes useful to be able to verify that certain settings are valid at compile time. An easy approach to do this is to pass the settings as non-type template arguments and use static_assert().

The code snippet below demonstrates an approach which doesn't require templates and cleanly separates the verification logic from the rest of the code.

namespace i2c {


//
//
// Driver Settings
//
//


struct Settings {
    int         sclPin = 0;
    int         sdaPin = 0;
    std::size_t freqHz = 1'000'000;
};


namespace detail {
struct SettingsChecker {
public:
    consteval SettingsChecker(Settings settings) : mSettings{settings} {
        bool validPinSettings =
            (settings.sclPin == 1 && settings.sdaPin == 2) ||
            (settings.sclPin == 3 && settings.sdaPin == 4);
        bool validFreqSettings =
            (settings.freqHz < 10'000'000) && (settings.freqHz > 100);

        // Exceptions thrown in consteval functions lead to compile time errors

        if (!validPinSettings) throw std::logic_error("Wrong I2C pin settings");
        if (!validFreqSettings)
            throw std::logic_error("Wrong I2C frequency settings");
    }

    constexpr operator Settings() const {
        return mSettings;
    }

private:
    Settings mSettings;
};
} // namespace detail


//
//
// Driver class
//
//


class Driver {
public:
    Driver(detail::SettingsChecker settings) : mSettings(settings) {
    }

private:
    Settings mSettings;
};


} // namespace i2c


//
//
// Usage
//
//


int main() {
    constexpr i2c::Settings settings1 = {
        .sclPin = 1, .sdaPin = 2, .freqHz = 1'000'000};

    i2c::Driver driver1{settings1}; // Compiles

    constexpr i2c::Settings settings2 = {
        .sclPin = 1, .sdaPin = 4, .freqHz = 1'000'000};

    i2c::Driver driver2{settings2}; // Compile time error
}

Compiling the above code leads to the following error:


compile-time-settings-check.cpp: In function int main():
compile-time-settings-check.cpp:88:34: error: call to consteval function i2c::detail::SettingsChecker(settings2) is not a constant expression
   88 |     i2c::Driver driver2{settings2}; // Compile-time error
      |                                  ^
compile-time-settings-check.cpp:88:34:   in constexpr expansion of i2c::detail::SettingsChecker(settings2)
compile-time-settings-check.cpp:36:32: error: expression <throw-expression> is not a constant expression
   36 |         if (!validPinSettings) throw std::logic_error("Wrong I2C pin settings");
      |