Keys, values, and prefixes are strings; empty strings are valid. Values are never nullopt. Everything stays in memory. There are no clocks, threads, persistence, or input type conversions.
Prototype: implement KeyValueStore() with independent storage. set(key, value) inserts or replaces and returns nullopt; get(key) returns its string value or nullopt; erase(key) returns true if it removed a key and false otherwise. Deleting and recreating a key is valid.
Final: implement TransactionalStore() with the same set, get, and delete behavior plus:
begin(): start a nested transaction from the current visible state and return the new depth.commit(): merge only the innermost transaction into its immediate parent, close that transaction, and return the new depth. An outer rollback must still undo an inner commit.rollback(): discard only the innermost transaction, close it, and return the new depth.depth(): return zero for the base view, one after a single unmatchedbegin(), and so on.snapshot(): return a fresh value record of the currently visible keys starting with the case-sensitive prefix, inserted into the result in ascending key order. An empty prefix includes every key. Mutating the result must not affect any transaction or base view.
Calling commit() or rollback() at depth zero raises std::logic_error and preserves the visible state and depth. Reads see all edits in the innermost view, including uncommitted writes and deletions. Duplicate set replaces a value. Separate instances share no state.
The public C++ API below is authoritative for field types and return values. erase is used where another language might use a method named delete, because delete is a C++ keyword. Returned records are value snapshots with the documented named fields; mutating a record or its nested vectors cannot affect the service.
class KeyValueStore { public:
KeyValueStore();
void set(const std::string& key,const std::string& value);
Opt<std::string> get(const std::string& key) const;
bool erase(const std::string& key);
};
class TransactionalStore { public:
TransactionalStore();
void set(const std::string& key,const std::string& value);
Opt<std::string> get(const std::string& key) const;
bool erase(const std::string& key);
Int begin();
Int commit();
Int rollback();
Int depth() const;
std::map<std::string,std::string> snapshot(const std::string& prefix="") const;
};Compile and run from this directory:
c++ -std=c++20 -O2 -Wall -Wextra -pedantic -pthread main.cpp -o practice
./practice --stage prototype
./practice --stage finalEdit exercise.hpp. Both stages start unfinished and must report failures until implemented. Keep main.cpp as the executable contract. The runner returns nonzero for a failed check, an unexpected exception, an invalid stage, or an empty suite. C++20 and only the standard library are required.
Complete the starter code, then run the checks.
Results and errors will appear here.
C++ runtime powered by Wasmer.