
David Abrahams <dave <at> boostpro.com> writes:
Isn't this a bit of an invitation to ODR violations?
// TU1 #include "MaximIO.hpp" #include "boost/range/iterator_range.hpp" #include "LibraryThatPrints.hpp"
print(some_iterator_range);
// TU2 #include "boost/range/iterator_range_io.hpp" #include "boost/range/iterator_range.hpp" #include "LibraryThatPrints.hpp"
print(some_iterator_range);
If the print template calls different IO functions in these two translation units, that's an ODR violation.
Hi David, that's a good question, and it also addresses boost.tuple and other libraries with detachable io (and detachable anything). In my case, there is no direct ODR violation, as there is actually no print function, but an operator<< living in a separate namespace instead. So when you include "MaximIO.hpp" and use "using MaximIO::operator<<" all your calls to operator<< will be resolved to MaximIO::operator<<, and everything else will be found with help of ADL. But if you include "boost/range/iterator_range_io.hpp" at the same time, you'll get the ambiguity error (that's what I faced). But you are right, if anybody defines a function print without specifying explicitly which io engine to use inside it (pretty common for a general print function), it's easy to get ODR violation here.
Just wondering if there isn't a deeper modularity issue lurking here to be solved.
Of course, when you start using template and inline functions, there is wide area for ODR violation (due to dependency of headers inclusion), for example, ODR can be violated somewhere deep inside my operator<< and the functions it calls. I don't see any 100% safe way to ensure ODR correctness and preserve high modularity, at least now. I see more or less awkward ways like #ifdefs for inclusion control, or passing explicit functors everywhere where we need anything custom instead of defining ODR-fragile entities. Traits are also not very safe here unless they are trivial and very carefully written. Thanks, Maxim P.S. Please write private e-mails to gmail.com