I've created the following class:
using namespace std;
typedef std::variant<long, bool> value;
class settings {
public:
template<class T>
void set(const string &name, const T &val) noexcept {
data_[name] = val;
}
template<class T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
if (data_.contains(name)) {
if (auto value = get_if<T>(&data_[name]); value) {
return *value;
}
}
return val;
}
private:
unordered_map<string, value> data_;
};
And this works fine, for example with this usage:
auto config = settings{};
config.set("foo", 100L);
config.set("bar", true);
cout << "foo: " << config.get_or_default("foo", 0L) << endl;
cout << "bar: " << config.get_or_default("bar", false) << endl;
cout << "foobar: " << config.get_or_default("foobar", 500L) << endl;
output
foo: 0
bar: 1
foobar: 500
If I try to use the method set
with something that is not valid for the type value
(bool
or long
):
auto config = settings{};
config.set("foo", 100.0);
We get an error like this:
\main.cpp(13): error C2679: binary '=': no operator found which takes a right-hand operand of type 'const T' (or there is no acceptable conversion)
with
[
T=double
]
variant(1200): note: could be 'std::variant<long,bool> &std::variant<long,bool>::operator =(std::variant<long,bool> &&)'
variant(1200): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(const std::variant<long,bool> &)'
variant(1042): note: or 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)'
main.cpp(12): note: 'std::variant<long,bool> &std::variant<long,bool>::operator =(_Ty &&) noexcept(<expr>)': could not deduce template argument for '__formal'
main.cpp(13): note: while trying to match the argument list '(std::variant<long,bool>, const T)'
with
[
T=double
]
main.cpp(32): note: see reference to function template instantiation 'void settings::set<double>(const std::string &,const T &) noexcept' being compiled
with
[
T=double
]
Similar using the method get_or_default
:
auto config = settings{};
cout << "foo: " << config.get_or_default("foo", 100) <<endl;
We get this error:
variant(1302): error C2338: static_assert failed: 'get_if<T>(variant<Types...> *) requires T to occur exactly once in Types. (N4835 [variant.get]/9)'
main.cpp(17): note: see reference to function template instantiation 'int *std::get_if<int,long,bool>(std::variant<long,bool> *) noexcept' being compiled
main.cpp(32): note: see reference to function template instantiation 'const T &settings::get_or_default<int>(const std::string &,const T &) noexcept' being compiled
with
[
T=int
]
These errors are fine, however I like to use c++ 20
concepts
to restrict the types for the method set
and get_or_default
to give a more simple error to the user of the class that indicates that the types valid are only bool
and long
.
Is possible as well to give a specific assertion message for those messages using only concepts? Very specifically, not third parties libraries.
Add a declaration:
#include <type_traits>
template<typename T>
concept is_value=std::is_same_v<T,long> || std::is_same_v<T,bool>;
and then make a tiny change to the template:
template<is_value T>
[[nodiscard]] inline const T &get_or_default(const string &name, const T &val) noexcept {
That's it. Make the same change to the other template function.