Reputation: 1724
Imagine we have a class responsible for setting values for SQL prepared statements like these:
void MySqlPreparedStatement::SetString(uint32_t paramIndex, const std::string& value);
void MySqlPreparedStatement::SetBinary(uint32_t paramIndex, const void* value, size_t length);
void MySqlPreparedStatement::SetUInt64(uint32_t paramIndex, const uint64_t value);
void MySqlPreparedStatement::SetInt64(uint32_t paramIndex, const int64_t value);
// ...
All these "set" methods have code that is common among them but still have some code that depends on the type of the value the user is setting in (e.g. m_parameters[paramIndex].buffer_type = MYSQL_TYPE_LONGLONG;
for 64 bit integers and m_parameters[paramIndex].buffer_type = MYSQL_TYPE_LONG;
for 32 bits integers).
In this case, which one is a better practice? Encapsulate all of these "set" methods in only one template method (which will make me create some switch/case on each accepted type-argument to get the correct value for buffer_type
) or just declare different methods for each accepted type of value like the declarations I showed above?
Upvotes: 1
Views: 90
Reputation: 25526
What is most suitable depends heavily on the code contained in the functions.
If you can clearly separate specific code and common identical code, then encapsulating this identical code in a separate function and call that one from smaller functions containing the specific parts might be more suitable (shortening signature a bit):
void setUInt64(uint64_t value)
{
// some code specific to uint64_t, maybe converting to binary or textual representation
setParameter(/*...*/, sizeof(value), MYSQL_TYPE_LONGLONG);
}
(According to your comments, that seems to be the case in your concrete example.)
Matter changes, though, if you have common code and specific code intermixed (extending the point of view):
void setInt32(int32_t value)
{
firstCommonFunction(/*...*/);
// some specific code
secondCommonFunction(/*...*/);
}
Would you really want to implement that pattern for every function? While above might be a corner case, you're better off with a template if the function gets even more complex:
template <typename T>
void setParameter(T const& value)
{
firstCommonFunction(/*...*/);
firstSpecificFunction<T>(/*...*/);
secondCommonFunction(/*...*/);
secondSpecificFunction<T>(/*...*/);
thirdCommonFunction(/*...*/);
thirdSpecificFunction<T>(/*...*/);
fourthCommonFunction(/*...*/);
}
Now a template assures that all your functions behave alike, calling common and specific functions whenever appropriate.
That's fine if your functions all look like that by design anyway. However, artificially enforcing such a design by all means solely for the sake of being able to have a template usually is not that a good idea...
Upvotes: 1