span
span copied to clipboard
Linking problems with multiple translation units
The internal function contract_violation() shares the same function signature between two different versions, depending on whether TCB_SPAN_THROW_ON_CONTRACT_VIOLATION is defined.
This is no problem until multiple translation units are linked together and TCB_SPAN_THROW_ON_CONTRACT_VIOLATION is defined in some but not all of them. The linker chooses one of the definitions and throws away the other, which means that the translation units that turned on TCB_SPAN_THROW_ON_CONTRACT_VIOLATION can end up erroneously calling the std::terminate version if that is the one the linker chose. Or vice-versa I presume. I see this in debug builds using GCC 8 or 10.
You can fix this problem by giving the throwing version a different function signature. For example: inline void contract_violation(const char* msg, int = 0)
It also seems to me that the throwing version should be [[noreturn]] like the std::terminate version, since it too never returns and [[noreturn]] because of throw was explicitly contemplated in many [[noreturn]] examples I've seen.
This is no problem until multiple translation units are linked together and TCB_SPAN_THROW_ON_CONTRACT_VIOLATION is defined in some but not all of them.
Defining different contract checking settings in different TUs and linking them together is going to be an ODR violation whichever way you look at it, because various member functions will have different definitions. In practise you might get away with it because those member functions are very likely to be inlined, but it's still a bad idea. Like NDEBUG, TCB_SPAN_THROW_ON_CONTRACT_VIOLATION should be set at the project level, not per-TU.
It also seems to me that the throwing version should be [[noreturn]] like the std::terminate version
Yes, I agree.