[optional] How to define boost::none?
Hi Everyone. I am puzzled with a tough development task. I wonder if anyone is able to find a good solution. I want to re-implement tag boost::none. The current implementation is using a pointer to member which allows a harmful conversion from literal 0. Try this: boost::optional<boost::rational<int>> orat = 0; // this creates a disengaged optional. I want to rewrite the definition with the following goals in mind: 1. boost::none_t is not implicitly convertible from literal 0; 2. The solution works in C++98 3. The solution is implementable in a header-only library. (defining some member variable in a CPP file is not an option. The only solution I was able to find so far is this: [code] struct none_t { enum creator_t {creator}; none_t(creator_t){} }; namespace { none_t none (none_t::creator); } [/code] It works, but I feel uncomfortable defining boost::none in every translation unit. Any better ideas are welcome. Regards, &rzej
On Wed, Nov 19, 2014 at 6:12 PM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Hi Everyone.
I am puzzled with a tough development task. I wonder if anyone is able to find a good solution.
I want to re-implement tag boost::none. The current implementation is using a pointer to member which allows a harmful conversion from literal 0. Try this:
boost::optional<boost::rational<int>> orat = 0; // this creates a disengaged optional.
I want to rewrite the definition with the following goals in mind: 1. boost::none_t is not implicitly convertible from literal 0; 2. The solution works in C++98 3. The solution is implementable in a header-only library. (defining some member variable in a CPP file is not an option.
The only solution I was able to find so far is this:
[code] struct none_t { enum creator_t {creator}; none_t(creator_t){} };
namespace { none_t none (none_t::creator); } [/code]
It works, but I feel uncomfortable defining boost::none in every translation unit. Any better ideas are welcome.
struct none_t {}; const none_t none = none_t(); Technically, this is dynamic initialization, but for our purpose it doesn't matter (there are no data members in none_t). Since none has internal linkage, it is translation unit-specific. Technically, this can cause ODR violations in some cases. To avoid that you can declare it like this: struct none_t {}; template< typename T > struct singleton { static const T instance; }; template< typename T > const T singleton< T >::instance = T(); const none_t& none = singleton< none_t >::instance;
Hi Andrey, On Wed, Nov 19, 2014 at 3:27 PM, Andrey Semashev wrote:
Since none has internal linkage, it is translation unit-specific. Technically, this can cause ODR violations in some cases. To avoid that you can declare it like this:
struct none_t {};
template< typename T > struct singleton { static const T instance; }; template< typename T > const T singleton< T >::instance = T();
// Do we need... namespace {
const none_t& none = singleton< none_t >::instance;
} // ? Thanks, Gareth ************************************************************************ The information contained in this message or any of its attachments may be confidential and is intended for the exclusive use of the addressee(s). Any disclosure, reproduction, distribution or other dissemination or use of this communication is strictly prohibited without the express permission of the sender. The views expressed in this email are those of the individual and not necessarily those of Sony or Sony affiliated companies. Sony email is for business use only. This email and any response may be monitored by Sony to be in compliance with Sony's global policies and standards
2014-11-19 16:40 GMT+01:00 Sylvester-Bradley, Gareth < Gareth.Sylvester-Bradley@eu.sony.com>:
Hi Andrey,
On Wed, Nov 19, 2014 at 3:27 PM, Andrey Semashev wrote:
Since none has internal linkage, it is translation unit-specific. Technically, this can cause ODR violations in some cases. To avoid that you can declare it like this:
struct none_t {};
template< typename T > struct singleton { static const T instance; }; template< typename T > const T singleton< T >::instance = T();
// Do we need... namespace {
const none_t& none = singleton< none_t >::instance;
} // ?
Thanks, Gareth
With anonymous namespace the variable becomes TU-local (translation unit-local), which guarantees no ODR violation. But instead repeats the same declaration in every TU.
On Wed, Nov 19, 2014 at 6:56 PM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
2014-11-19 16:40 GMT+01:00 Sylvester-Bradley, Gareth < Gareth.Sylvester-Bradley@eu.sony.com>:
Hi Andrey,
On Wed, Nov 19, 2014 at 3:27 PM, Andrey Semashev wrote:
Since none has internal linkage, it is translation unit-specific. Technically, this can cause ODR violations in some cases. To avoid that you can declare it like this:
struct none_t {};
template< typename T > struct singleton { static const T instance; }; template< typename T > const T singleton< T >::instance = T();
// Do we need... namespace {
const none_t& none = singleton< none_t >::instance;
} // ?
Thanks, Gareth
With anonymous namespace the variable becomes TU-local (translation unit-local), which guarantees no ODR violation. But instead repeats the same declaration in every TU.
The reference becomes TU-local, the singleton instance has external linkage, so it's shared between TUs. I'm not sure if the reference declared this way actually adds anything to the binary size.
On Wed, Nov 19, 2014 at 6:40 PM, Sylvester-Bradley, Gareth <Gareth.Sylvester-Bradley@eu.sony.com> wrote:
Hi Andrey,
On Wed, Nov 19, 2014 at 3:27 PM, Andrey Semashev wrote:
Since none has internal linkage, it is translation unit-specific. Technically, this can cause ODR violations in some cases. To avoid that you can declare it like this:
struct none_t {};
template< typename T > struct singleton { static const T instance; }; template< typename T > const T singleton< T >::instance = T();
// Do we need... namespace {
const none_t& none = singleton< none_t >::instance;
} // ?
It seems so. I thought references don't have linkage but it looks like I was wrong. It doesn't add ODR violations though.
2014-11-19 16:12 GMT+01:00 Andrzej Krzemienski <akrzemi1@gmail.com>:
Hi Everyone.
I am puzzled with a tough development task. I wonder if anyone is able to find a good solution.
I want to re-implement tag boost::none. The current implementation is using a pointer to member which allows a harmful conversion from literal 0. Try this:
boost::optional<boost::rational<int>> orat = 0; // this creates a disengaged optional.
I want to rewrite the definition with the following goals in mind: 1. boost::none_t is not implicitly convertible from literal 0; 2. The solution works in C++98 3. The solution is implementable in a header-only library. (defining some member variable in a CPP file is not an option.
The only solution I was able to find so far is this:
[code] struct none_t { enum creator_t {creator}; none_t(creator_t){} };
namespace { none_t none (none_t::creator); } [/code]
It works, but I feel uncomfortable defining boost::none in every translation unit. Any better ideas are welcome.
Regards, &rzej
Hi Andrzej, Why do you need the creator_t? What's wrong with a user creating other instances of none_t? Did you forget to make none const? And then, why wouldn't a straightforward solution work: struct none_t {}; none_t const none = {}
2014-11-19 16:28 GMT+01:00 Krzysztof Czainski <1czajnik@gmail.com>:
2014-11-19 16:12 GMT+01:00 Andrzej Krzemienski <akrzemi1@gmail.com>:
Hi Everyone.
I am puzzled with a tough development task. I wonder if anyone is able to find a good solution.
I want to re-implement tag boost::none. The current implementation is using a pointer to member which allows a harmful conversion from literal 0. Try this:
boost::optional<boost::rational<int>> orat = 0; // this creates a disengaged optional.
I want to rewrite the definition with the following goals in mind: 1. boost::none_t is not implicitly convertible from literal 0; 2. The solution works in C++98 3. The solution is implementable in a header-only library. (defining some member variable in a CPP file is not an option.
The only solution I was able to find so far is this:
[code] struct none_t { enum creator_t {creator}; none_t(creator_t){} };
namespace { none_t none (none_t::creator); } [/code]
It works, but I feel uncomfortable defining boost::none in every translation unit. Any better ideas are welcome.
Regards, &rzej
Hi Andrzej,
Why do you need the creator_t? What's wrong with a user creating other instances of none_t?
Did you forget to make none const?
And then, why wouldn't a straightforward solution work:
struct none_t {};
none_t const none = {}
I think it validates ODR, or doesn't it?
Andrzej Krzemienski wrote:
2014-11-19 16:28 GMT+01:00 Krzysztof Czainski <1czajnik@gmail.com>:
And then, why wouldn't a straightforward solution work:
struct none_t {};
none_t const none = {}
I think it validates ODR, or doesn't it?
It does violate ODR at places (without any ill effects AFAIK). Bind's placeholders have the same problem. One option is to use struct none_rt {}; typedef none_rt (*none_t)(); inline none_rt none() { return none_rt(); } Another is to declare none as constexpr: struct none_t {}; constexpr none_t none = {};
2014-11-19 16:43 GMT+01:00 Peter Dimov <lists@pdimov.com>:
Andrzej Krzemienski wrote:
2014-11-19 16:28 GMT+01:00 Krzysztof Czainski <1czajnik@gmail.com>:
And then, why wouldn't a straightforward solution work:
struct none_t {};
none_t const none = {}
I think it validates ODR, or doesn't it?
It does violate ODR at places (without any ill effects AFAIK). Bind's placeholders have the same problem.
Hmm, looks like an option (although technically this is an UB)
One option is to use
struct none_rt {};
typedef none_rt (*none_t)();
inline none_rt none() { return none_rt(); }
Not an option. According to GCC, the following compiles: void d(none_t) {} int main() { d(0); } And this is exactly what I try to avoid.
Another is to declare none as constexpr:
struct none_t {};
constexpr none_t none = {};
Not an option: requires C++11
Andrzej Krzemienski wrote:
One option is to use
struct none_rt {};
typedef none_rt (*none_t)();
inline none_rt none() { return none_rt(); }
Not an option. According to GCC, the following compiles:
void d(none_t) {} int main() { d(0); }
Right, sorry, I forgot. You can make that typedef none_rt (&none_t)(); and it will reject 0, but then 'none' will not pass through template parameters. void d( none_t ) {} template< class T > void e( T t ) { d( t ); } int main() { e( none ); } Not sure if you need that, but you probably do.
2014-11-19 16:58 GMT+01:00 Andrzej Krzemienski <akrzemi1@gmail.com>:
2014-11-19 16:43 GMT+01:00 Peter Dimov <lists@pdimov.com>:
Andrzej Krzemienski wrote:
2014-11-19 16:28 GMT+01:00 Krzysztof Czainski <1czajnik@gmail.com>:
And then, why wouldn't a straightforward solution work:
struct none_t {};
none_t const none = {}
I think it validates ODR, or doesn't it?
It does violate ODR at places (without any ill effects AFAIK). Bind's placeholders have the same problem.
Hmm, looks like an option (although technically this is an UB)
But, what would be a practical recommendation for my problem? Is having a "benign" UB like the above an acceptable solution for a Boost library?
Is it really such a problem to have this object in every translation unit? Is it beyond current linker technology to merge these identical copies, thus preventing binary bloat? If it is, you could use something like the following solution: template<typename T> class optional { public: optional(none_t) { /* whatever */ } private: enum dummy{}; optional(dummy *); }; This causes users attempting to initialize optional with a literal 0 or nullptr to receive an error about ambiguous overloading. However, it still allows it to be used as: optional<int> oi(0); if you also have a public constructor that accepts a T const & (which you do). The only tricky part is code like the following: optional<int *> op0(0); optional<int *> opn(nullptr); Both of those give ambiguous overloads. The first, from integer literal 0, already does not work, because the template parameter in your current constructor is deduced as int, and there is no implicit conversion from int to int *. C++03 users would have to say something like static_cast<int *>(0). The second, however, currently compiles, and we probably want to preserve this behavior. Note that if we had said optional<int *> opn = nullptr, it would fail. We probably want that to work just like explicit construction. This means we need to selectively enable and disable some of these constructors under certain conditions. I believe the following will work for your needs: // Same none_t and none definition as in boost now, just so people can see it without looking it up namespace detail { struct none_helper{}; } typedef int detail::none_helper::*none_t ; none_t const none = (static_cast<none_t>(0)) ; template<typename T> class optional { public: template<typename U> optional(U, typename std::enable_if<std::is_same<U, none_t>::value, int>::type * = 0): m_b(false) {} optional(T): m_b(true) {} operator bool() const { return m_b; } private: bool m_b; enum dummy{}; template<typename U> optional(U, typename std::enable_if<std::is_same<U, dummy *>::value, int>::type * = 0); }; I used C++11 type traits because I can never remember when I need to use the _c versions of boost type traits, but it should be fairly straightforward to translate that for someone more familiar with the boost versions. The following code compiles and runs. Commented out lines do not compile. class C {}; int main() { boost::optional<int> bi(0); assert(bi); boost::optional<int> bia = 0; assert(bia); // boost::optional<C> bc(0); boost::optional<C> bca = 0; assert(!bca); boost::optional<int *> bpn(nullptr); assert(bpn); // boost::optional<int *> bpna = nullptr; boost::optional<int *> bp0c(static_cast<int *>(0)); assert(bp0c); boost::optional<int *> bp0ca = static_cast<int *>(0); assert(bp0ca); // boost::optional<int *> bp0(0); // boost::optional<int *> bpoa = 0; optional<int> ni(0); assert(ni); optional<int> nia = 0; assert(nia); // optional<C> nc(0); // optional<C> nca = 0; optional<C> ncu(none); assert(!ncu); optional<C> ncua = none; assert(!ncua); optional<int *> npn(nullptr); assert(npn); optional<int *> npna = nullptr; assert(npna); optional<int *> np0c(static_cast<int *>(0)); assert(np0c); optional<int *> np0ca = static_cast<int *>(0); assert(np0ca); optional<int *> np0(0); assert(np0); optional<int *> np0a = 0; assert(np0a); optional<int *> npu(none); assert(!npu); optional<int *> npua = none; assert(!npua); } Are all of these test cases the correct result?
On Thu, Nov 20, 2014 at 5:54 PM, David Stone <david@doublewise.net> wrote:
Is it really such a problem to have this object in every translation unit? Is it beyond current linker technology to merge these identical copies, thus preventing binary bloat?
The linker must not merge them because these objects must have distinct addresses. This might not be the case for references though.
On Thu, Nov 20, 2014 at 7:59 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
On Thu, Nov 20, 2014 at 5:54 PM, David Stone <david@doublewise.net> wrote:
Is it really such a problem to have this object in every translation unit? Is it beyond current linker technology to merge these identical copies, thus preventing binary bloat?
The linker must not merge them because these objects must have distinct addresses. This might not be the case for references though.
Only if you take their address. If no one is saying &boost::none or passing it by reference to a function that takes its address, no one can tell the difference.
participants (6)
-
Andrey Semashev
-
Andrzej Krzemienski
-
David Stone
-
Krzysztof Czainski
-
Peter Dimov
-
Sylvester-Bradley, Gareth