Boost logo

Boost :

Subject: Re: [boost] Boost.Conversion review
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2011-08-21 06:31:49


Le 17/08/11 01:43, Vicente J. Botet Escriba a écrit :
> Le 16/08/11 23:29, John Bytheway a écrit :
>> On 16/08/11 15:59, Gordon Woodhull wrote:
>>> Hi all,
>>>
>>> The review of Vicente Botet Escriba's Conversion library starts this
>>> Saturday August 20 and continues through August 29.
>>>
>>> This library provides a general system for type-to-type conversion.
>>> As such, it can be thought of either as a substitute for overloading
>>> static_cast, or as an alternative to lexical_cast when the
>>> intermediate conversion to text is not wanted.
>>>
>>> Your review and/or discussion would be greatly appreciated. As
>>> always, please post to the main Boost list if possible, or the
>>> boost-users list, or you can send your review directly to the Review
>>> Managers.
>> The primary concern I always have with libraries such as this one is
>> that they promote ODR violations. In particular, consider:
>>
>> library A defines type TA
>>
>> library B defines type TB
>>
>> library C defines a conversion from TA to TB
>>
>> library D defines a conversion from TA to TB
>>
>> Now libraries C and D are incompatible; they cannot both be used in the
>> same program without ODR violation.
> You are right and I have no solution to this big issue. I must add
> this to the documentation.
>
> I think that the same case appears when
>
> library T defines a trait type tt that must be specialized by the user.
> library A defines type TA
> library C needs to specialize the type trait tt for TA
> library D needs to specialize the type trait tt for TA
>
> Now libraries C and D are incompatible for the same reason.
>
> An organization could choose to organize his libraries in a way that
> make possible to avoid the odr in an easy way. But it is not simple to
> formalize some guidelines that can be shared between different
> organizations.
>
> I don't know, even if the following is not a solution to the issue,
> maybe the library could suggest to the library author defining the
> conversion to add a static variable that could be used to detect the
> odr violations at link time.
>
> I don't remember who, suggested that library C should define classes
> C::TA and C::TB inheriting from TA and TB so one knows the other and
> conversion can be defined intrinsicaly to these specific classes. I
> have not explored the usage, advantages and liabilities of this approach.
>> This means, if I am writing a library, then I cannot safely define a
>> conversion between types both of which are not in my library, because it
>> would make my library incompatible with any other library that also
>> defines such a conversion.
>
>>
>> But, on the other hand, if one of the types *is* in my library, then I
>> can probably make do with a conversion constructor or conversion
>> operator (except perhaps if I want an explicit conversion operator and
>> want to support compilers without those).
>>
>> So, as I see it, conversions can only be defined in two situations:
>>
>> - When writing non-library code (i.e. code that will not be combined
>> with other code over which the author has no control).
> Yes, here there is no problem as the author/organization can manage
> with which code is included in the executable.
>> - As a stop-gap substitute for explicit conversion operators.
> This was one of the initial motivation of the library.
>>
>> Is it indeed intended only to allow conversions to be defined in these
>> limited circumstances? If so, then the documentation should state that
>> clearly. If not, how do you intend to avoid ODR violations?
>>
> You are completly right and during a good period I was tempted to
> withdraw the library submission. However,as you pointed out, there are
> some context in which the library is useful so I have persisted in the
> development.
>
> Thanks for raising (again) this big issue, which could be one of the
> reasons to reject the library.
>
>
Hi,

I have added a section to the documentation containing a possible
approach that could address this issue.

I have added it below.

Best,
Vicente

How can libraries define and use conversions between unrelated types?

Header-only libraries can define conversion between unrelated types with
no ODR violation issue.

Imagine the library C needs a conversion from `TA` to `TB` and that this
conversion is defined in file `C_TA_2_TB.h`

   // C_TA_2_TB.hpp

   #include <boost/conversion.hpp>
   #include <A/TA.hpp>
   #include <B/TB.hpp>
   // specific definition of the conversion
   // ...

The problem appears when the library needs to use itself this or another
specific conversion.
In order to avoid the problem the library can not include the file
defining the specific conversion.

   // C_Uses_T1_2_TB.hpp
   // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   // WARNING !!! The end user of this file must ensure that
   // a single definition of the conversion from TA to TB is included in
each .cpp file that uses it.
   // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   #include <boost/conversion.hpp>
   #include "A/TA.hpp"
   #include "B/TB.hpp"

   namespace C {
     // A function using the specific conversion implicitly
     void depend_on_TA_to_TB_conversion()
     {
       // ...
       // uses generic conversion from TA to TB
       TB b=Boost::conversion::convert_to<TB>(a);
       // ...
     }
   }

Note that the header `C_Uses_T1_2_TB.hpp` couldn't compile without
including the definition of the specific conversion,
as for example the provided by C in `C_TA_2_TB.hpp`.

In this way, the library doesn't depend directly to the specific
conversion definition.
End users using the header file that depends implicitly of the specific
conversion could and must ensure that a unique
definition of the specific conversion is used by including one of the
available definitions in each `.cpp` file using the depending
`Uses_T1_2_TB.hpp` header file.

   // E_Uses_C_and_D_functions_depending_on_TA_to_TB_conversion.cpp

   // Include either the C or D definition but always the same
   #include <C_TA_2_TB.hpp>
   #include "C_Uses_T1_2_TB.hpp"
   #include "D_Uses_T1_2_TB.hpp"

   // use C:: depend_on_TA_to_TB_conversion()

This is a little bit constraining, but it works in most of the cases. Of
course it assumes that the available conversion implementations are all
equivalent.
If this is not the case, this doesn't work of course.

Non header only libraries could define conversion without ODR violation
as long as the conversion is defined completely in a header file.
However they can not use specific conversions in any `.cpp` file as this
will make a explicit dependency to the provided conversion
definition, promoting ODR violations.
That means that any uses of the specific conversion must be included in
a header file as is done in the case of header-only libraries.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk