#include <cassert>
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

class Config {
public:
    explicit Config(std::vector<std::string> initial)
        : values_(std::move(initial)) {
        validate(values_);
    }

    const std::vector<std::string>& values() const noexcept {
        return values_;
    }

    void replace(const std::vector<std::string>& next, int fail_point = 0) {
        auto candidate = next;  // Allocation/copy may throw; values_ is unchanged.
        if (fail_point == 1) {
            throw std::runtime_error("injected after copy");
        }
        validate(candidate);
        if (fail_point == 2) {
            throw std::runtime_error("injected before commit");
        }
        // std::vector<std::string> uses the default allocator here.
        // swap is the non-throwing commit; there are no concurrent readers.
        values_.swap(candidate);
    }

private:
    static void validate(const std::vector<std::string>& values) {
        if (values.empty()) {
            throw std::invalid_argument("empty configuration");
        }
        for (const auto& value : values) {
            if (value.empty()) {
                throw std::invalid_argument("empty entry");
            }
        }
    }
    std::vector<std::string> values_;
};

static void check(bool condition) {
    assert(condition);
    if (!condition) {
        throw std::logic_error("check failed");
    }
}

int main() {
    Config config({"old-a", "old-b"});
    const auto original = config.values();
    const std::vector<std::string> replacement{"new-a", "new-b"};

    for (int point : {1, 2}) {
        bool caught = false;
        try {
            config.replace(replacement, point);
        } catch (const std::runtime_error&) {
            caught = true;
        }
        check(caught);
        check(config.values() == original);
    }

    bool invalid_caught = false;
    try {
        config.replace({"valid", ""});
    } catch (const std::invalid_argument&) {
        invalid_caught = true;
    }
    check(invalid_caught);
    check(config.values() == original);

    config.replace(replacement);
    check(config.values() == replacement);
    std::cout << "transaction checks passed\n";
}
