Which exception is thrown when is not as consistent as it should be. For example, Data::value(i) throws std::domain_error if the index is out of bounds, while Data::operator[] throws std::out_of_range. The std::domain_error exception is intended for numerical functions (e.g. negative input to sqrt()) and should not be used here. Also, Message::next_timestamp() throws std::logic_error if there is no next timestamp, while Data::operator throws std::runtime_error if the contained object is not a dictionary, which is also a precondition violation.
It's probably easier for the users the fewer kinds of errors we have, so this should be cleaned up and simplified. Do we need anything beyond std::bad_cast (Data or SettingValue doesn't contain expected type), std::out_of_range (invalid index or missing key), and std::runtime_error (everything else)?
Which exception is thrown when is not as consistent as it should be. For example, Data::value(i) throws std::domain_error if the index is out of bounds, while Data::operator[] throws std::out_of_range. The std::domain_error exception is intended for numerical functions (e.g. negative input to sqrt()) and should not be used here. Also, Message::next_timestamp() throws std::logic_error if there is no next timestamp, while Data::operator throws std::runtime_error if the contained object is not a dictionary, which is also a precondition violation.
It's probably easier for the users the fewer kinds of errors we have, so this should be cleaned up and simplified. Do we need anything beyond std::bad_cast (Data or SettingValue doesn't contain expected type), std::out_of_range (invalid index or missing key), and std::runtime_error (everything else)?