I want to check if a data field is valid (valid means being not null and not filled with the default value)
Basically
return (!connector->IsNull(field_id) and connector->Get<type>Default(field_id, default_value))
But "type" can be one of many types (string, int64, etc...) so there are 5-6 different functions. I made a helper function for it and I'm trying to pass in the relevant GetDefault...
template<typename T> bool IsValidField(std::unique_ptr<Connector>& connector, const std::function<T(int, T)> &GetDefault, int field_id, T default_value){
return (!connector->IsNull(field_id) && connection->GetDefault(field_id, default_value) != default_value);
}
And I'm calling the helper function with....
IsValidField(connector, connector->GetStringWithDefault,20,"")
I get the error "error: reference to non-static member function must be called
" because GetStringWithDefault isnt a static function, how do I fix this?
Alternately, is there a way of making it slightly less awkward?
Two possible solutions, one is using std::bind
and the other is using std::mem_fn
.
For the solution using std::bind
it could look something like
template<typename T, typename F>
bool IsValidField(std::unique_ptr<Connector>& connector, F GetDefault,
int field_id, T default_value)
{
return (!connector->IsNull(field_id) &&
GetDefault(field_id, default_value) != default_value);
}
Then call it like
IsValidField(
IsValidField(connector,
std::bind(&ClassForConnector::GetStringWithDefault, _1, _2),
20,"");
For the std::mem_fn
solution, maybe something like
template<typename T, typename F>
bool IsValidField(std::unique_ptr<Connector>& connector, F GetDefault,
int field_id, T default_value)
{
return (!connector->IsNull(field_id) &&
GetDefault(connector, field_id, default_value) != default_value);
}
And call it like
IsValidField(connector,
std::mem_fn(&ClassForConnector::GetStringWithDefault),
20,"");
Perhaps not the most neat, but the most trivial solution seems to be to wrap everything into lambda:
IsValidField(connector, [connector]() -> T {return connector->GetStringWithDefault(20, "")})
But then you need to adjust the IsValidField
signature accordingly.
There are two things going on here. Do one thing at a time.
Clean up how to call the GetStringWithDefault
, so you can get something based off its type without knowing the name of the function, as follows:
namespace impl {
template<class T, T Connector::*Method(int, T const&)>
struct GetWithDefault {
T operator()(Connector const& connector, int id, T const& default) const {
return (connector.*Method)(id, default);
};
}
template<class T>
struct GetWithDefault;
template<>struct GetWithDefault<std::string>:
impl::GetWithDefault<std::string, &Connector::GetStringWithDefault>
{};
repeat for each supported type. Now, GetWithDefault<Bob>{}(*connector, id, default_bob)
does what you want.
Now the null version:
template<class T>
bool IsValidField(Connector const& connector, int id, T const& default_value) {
return !connector.IsNull(id) && (GetWithDefault<T>{}(connector, id, default_value)!=default_value);
}
};
This moves the association between T
and the gettor into a helper type GetWithDefault
, where we implement it via explicit specialization (sort of like a traits class).