0

I have code that should be compiled with gcc 4.7, which have not bad c++11 supports, but lacks c++14 and c++17.

And I want to use std::optional and std::variant in my code, with fallback for boost::optional and boost::variant for gcc 4.7

What the standard way to achieve this?

I see two variants:

  1. Wrapper around variant, it may be macro like: #define VARIANT_TYPE boost::variant or namespace my_code { using variant = boost::variant }
  2. extend std namespace

The first one is not good, because of my code will use my_code::variant, instead of std::variant. The second one looks more clear for me.

The 2 may be UB in general case What are the reasons that extending the std namespace is considered undefined behavior?

But what about my specific case? I know what exactly version of gcc has variant/optional, I can check it on configure stage of build. For all other compilers/standard c++ library I can require c++17 support and not use my hack that convert boost::optional to std::optional.

Can I still catch some exotic problems?

user1244932
  • 7,352
  • 5
  • 46
  • 103
  • 3
    What's the point of switching implementations? Just use boost unconditionally. – HolyBlackCat Jan 26 '18 at 22:52
  • 3
    I would do `using variant_type = std::variant;`. Avoid a macro for this. Also you should never extend `std` except where the standard allows. – Galik Jan 26 '18 at 22:55
  • @HolyBlackCat Because of it is library, and I do not want create external dependency on boost for users who has `c++17` compatible compilers. – user1244932 Jan 26 '18 at 23:10
  • @Galik But for specific compiler and libstdc++ not for general case – user1244932 Jan 26 '18 at 23:12

2 Answers2

3

You might use something like:

#ifdef NEED_WORKAROUND

# include <boost/optional.hpp>
# include <boost/variant.hpp>

namespace workaround
{
    namespace std
    {
        using boost::optional;
        using boost::variant;
    }
}
using namespace workaround;
#else
# include <optional>
# include <variant>
#endif

So usage with ::std::variant<..> won't work
but simple std::variant<..> would mostly behave similar (you might have issue with ADL).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Why would you have any issues with ADL? `using`-declarations don't influence ADL in any way. `using`-directives only influence it in so far that they might bring names into scope that are *not* ADL associated (like when calling functions for primitive types that have no associated namespaces) – sehe Jan 27 '18 at 12:29
  • @sehe: for example for code like: `find(carray, carray+N, std::optional(42))` we don't need `std::find` (without the workaround, but we need it with). So there is an "issue". – Jarod42 Jan 29 '18 at 08:24
1

One way to get close to what you want is to find a library that does it for you.

For example, Google's Abseil does this. Unfortunately, their Supported Platforms page shows that they need gcc 4.8+, as they require C++11. But if you can meet their requirements, then you can use absl::optional in place of std::optional, and if std::optional exists, then absl::optional is std::optional, otherwise it would be equivalent to std::optional.

That you write absl::optional instead of std::optional is inconsequential; readers of your codebase can easily learn that they are "the same thing." I strongly recommend not trying to put this into the std namespace.


Doing this yourself is unpleasant, but not impossible. You'd need 2 things:

  • A way to tell if std::optional exists
  • An implementation of std::optional in another namespace that works on the version of the compiler you want to support (note that the boost versions aren't necessarily completely equivalent to the std versions)

Then, you could write your workaround like in Option 1, and write something like:

#if STD_OPTIONAL_EXISTS

namespace compat {
    template <typename... Ts>
    using optional = std::optional<Ts...>;
}

#else

namespace compat {
    template <typename... Ts>
    using optional = boost::optional<Ts...>;
}

#endif
Justin
  • 24,288
  • 12
  • 92
  • 142