There are different ways to design it, but a simple version could be a function that takes 2 operands and 1 result pointer as inputs, and returns boolean (true if success, false if it overflowed):
bool SafeAddIntInt(int32_t x, int32_t y, int32_t *r);
so the caller could say
int32_t result;
if (SafeAddIntInt(x, y, &result)) {
// do something with result
} else {
// handle overflow
}
An even simpler version could just abort on over/underflow.
Note that there is no such thing as integer underflow. INT_MIN-1 is an overflow just as much as INT_MAX+1.
Only floats can suffer from underflow, which happens when you want to represent a number whose absolute value is smaller than the floating point precision can allow (e.g. trying to represent 1/2^32 in a 32-bit float).