I have a design case that I want to discuss. I have a following function definition where uppercase are macros:
errorType someFunc(params) {
INTEGRITY_CHECK(params)
SET_DATA_1(params, 0, 1, 2)
SET_DATA_2(params, 'a', 'b', 'c')
}
These macros are defined in a common header file which is called by other files. Each of these macros are basically function calls that conditionally return.
#define INTEGRITY_CHECK(val) \
if (is_empty_func(val)) \
{ \
LOG_ERR("Empty Data"); \
return errorCode_1; \
}
#define SET_DATA_1(params, \
v1, \
v2, \
v2) \
if (errorType retCode = some_function_set_data_1(params, \
v1,v2,v3); \
retCode != errorCode_success) \
{ \
return errorCode_2; \
}
#define SET_DATA_2(params, \
v1, \
v2, \
v2) \
if (errorType retCode = some_function_set_data_2(params, \
v1,v2,v3); \
retCode != errorCode_success) \
{ \
return errorCode_3; \
}
Params is a pointer to a struct.
Sorry for the ambiguity and vagueness.
I understand the use case for the above design is not having repetitive code. We can conditionally return from a function as macros are expanded and esentially become the function. Is there a modern C++ way / using functions for this pattern that is succinct ? Or this is the use case for macros ?
Why macros are helpful ? Let's say we replace macro with a function, it'll return a value and in the function where macros are called, we have to recheck the returned value with an if.
The most obvious replacement for a sequence of conditional returns is going to be exceptions.
This is - at least if the failure paths are really exceptional (ie, unusual) - exactly what they're designed for.
If the public interface needs to remain unchanged, it's easy enough to write a wrapper like
errorType someFunc(params) {
try {
return someFuncImpl(params);
} catch (Exception const& ex) {
return ex.errorCode;
}
}
All the macros can then be replaced by equivalent functions that simply throw on the error path.