Boost logo

Boost :

From: tobi_at_[hidden]
Date: 2024-04-06 10:32:42


Am 2024-03-27 17:40, schrieb Andrey Semashev via Boost:
> On 3/27/24 17:01, Tobias Loew via Boost wrote:
>> Am 2024-03-26 18:50, schrieb Andrey Semashev via Boost:
>>> On 3/26/24 18:53, Tobias Loew via Boost wrote:
>>>>
>>>> The library now has a macro-option to prevent importing all
>>>> operators
>>>> into the global
>>>> namespace (BOOST_FLAGS_NO_GLOBAL_USING).
>>>
>>> I think the no-global-namespace should be the only option, and there
>>> should be no such macro. Otherwise, there is risk of configuration
>>> mismatch and ODR violations.
>>>
>>> Enums are often used in library interfaces, and offering this
>>> configuration macro may pose a problem. For example, if a library A
>>> wants to define its public enums with this macro defined, and its
>>> user B
>>> wants to define its own enums without this macro then there will be a
>>> compatibility issue.
>>
>> Thanks for pointing out this, even though I cannot see ODR violations:
>> the
>> definition of the operators/utility functions are always the same and
>> reside
>> in namespace boost::flags, the import into other namespaces is done
>> via
>> using declarations which (IIRC) are irrelevant to ODR.
>
> It may be relevant to ODR if the using-declaration changes the operator
> found by name lookup.

I changed the enabling-function at namespace scope to a macro that also
imports all the operators into the current namespace.
So, all operator call can be found by ADL.
As this is not possible at class scope, I swallowed the toad (no harm to
amphibia was done) and made a macro that enables and adds forwarding
friend functions. This is the only way I could find to ensure ADL for
enums in classes with just a single macro right after the enum
definition
inside the class.

>> One more word to, why I imported everything into global namespaces: I
>> wanted
>> the opt-in be a single definition and NOT a macro (just to prove, that
>> it's
>> possible). So, to make it work, I had to import all
>> operators/functions
>> into
>> global namespace.
>
> I understand. But being able to do it doesn't necessarily make it the
> best solution. Introducing things in global namespace is rather
> intrusive, and it has bitten us in the back in the past. I think,
> libraries should avoid putting their stuff in the global namespace at
> all costs, and the "enable" macro approach seems like an acceptable
> alternative to me.

Agree, see above.

>> From my perspective, this library is more like a core-language
>> extension:
>> it introduces flags to the language in a generic way, and therefore
>> enhances the global operators with the respective functionality for
>> enabled
>> enums.
>
> Even core language extension libraries should avoid polluting the
> global
> namespace. Take existing Boost libraries for example: Boost.ForEach,
> Boost.Lambda2, Boost.StaticAssert.
>
> By the way, we already have boost/detail/bitmask.hpp that provides
> bitwise operators for enums, and it is also designed to be used in the
> enum's namespace (i.e. without polluting the global namespace).

That's interesting. I use the Boost libraries (many of them) now for
almost 20 years and never came over that file.
I've added it to the "Other flags-like enum implementations" section
in my docs.
Nevertheless, my library is superior to bitmask.hpp in several ways:

- complement can be distinguished
- enhanced type-safety for unscoped enums: with my library an enabled
    enum cannot be combined with a non-compatible enum for binary
operators
- bitmask.hpp may invoke UB for operator~ (Boost.Flags does not, when
    complement is not disabled)
- support for "large" enums (size > 32 bits), bitmask.hpp silently
     returns wrong values
- everything is constexpr

>> The utilities are a bunch of functions
>> - for handling flags as a multi-dim Boolean algebra: tests for
>>   inclusion, intersection
>> - easy interface for modifying flags depending on a bool, e.g.
>>   make_if(E e, bool set)               for     set ? e : E{}
>>   remove_if(E e, E mod, bool remove)   for     remove ? e & ~mod : e
>>
>> I see them as part of the flags interface, so in my original version
>> of
>> the library they were always imported into global namespace. But I
>> wanted
>> the import to be optional, in case others see it different.
>
> This sounds more like a collection of algorithms on enums. In this
> case,
> I think, they are better placed in Boost.Flags namespace, and there is
> no need to import them, other than to omit the namespace when calling
> them.


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