All keys, fields, and values are strings; empty strings are valid and nullopt denotes absence. No disk, network, wall time, or threading is needed.
Prototype: implement RecordStore() with set(), get(), and scan(). Setting replaces an existing field. Scan includes fields starting with the case-sensitive prefix and sorts by field name. Missing records produce nullopt or []. Returned lists are fresh snapshots; records and instances are independent.
Final: implement TemporalDatabase():
- Every public method takes a nonnegative integer
ts. Valid calls, including reads and unsuccessful conditional/deletion calls, advance the accepted time. Equal timestamps are allowed; a later call at the same timestamp wins. A smaller timestamp raisesstd::invalid_argumentwithout changing history or accepted time. Booleans and noninteger arguments are outside this exercise's input domain. set()writes a version.ttl=nulloptmeans no expiration; otherwisettlis a positive integer. A write attsis visible on[ts, ts + ttl). Replacing a field replaces its lifetime too. A nonpositive TTL raisesstd::invalid_argumentbefore any time or state change.get()reads atts.erase()appends a deletion marker only when the field is currently visible. Missing or expired fields returnfalse.compare_and_set()writes the new value and lifetime only if the current visible value equals the stringexpected. Missing/expired fields returnfalse. TTL validation occurs even if comparison would fail.scan(ts, key, prefix="")returns a fresh list of visible(field, value)tuples, filtered by case-sensitive prefix and sorted by field name.get_at()reads history at integerat, where0 <= at <= ts. Invalidatraisesstd::invalid_argumentwithout advancing accepted time. Reads before creation, at/after expiration, or after a deletion returnnullopt. An expired newer version never reveals an older version. History reflects operations already processed; later same-timestamp writes become visible to later calls.
Rejected operations preserve history. All validation errors preserve accepted time as well. Separate instances share nothing.
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 RecordStore { public:
RecordStore();
void set(const std::string& key,const std::string& field,const std::string& value);
Opt<std::string> get(const std::string& key,const std::string& field) const;
Vec<std::pair<std::string,std::string>> scan(const std::string& key,const std::string& prefix="") const;
};
class TemporalDatabase { public:
TemporalDatabase();
void set(Int ts,const std::string& key,const std::string& field,const std::string& value,Opt<Int> ttl=std::nullopt);
Opt<std::string> get(Int ts,const std::string& key,const std::string& field);
bool erase(Int ts,const std::string& key,const std::string& field);
bool compare_and_set(Int ts,const std::string& key,const std::string& field,const std::string& expected,const std::string& value,Opt<Int> ttl=std::nullopt);
Vec<std::pair<std::string,std::string>> scan(Int ts,const std::string& key,const std::string& prefix="");
Opt<std::string> get_at(Int ts,const std::string& key,const std::string& field,Int at);
};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.