23
\$\begingroup\$

From this Stack Overflow answer, I learned that C++17 will have __has_include, which can

[make] migrating from experimental to std almost seamless

This still leaves the question of how to do it. Doing something like namespace std { using namespace std::experimental; } is undefined behaviour, so I came up with this method:

optional.h demonstration on coliru

#pragma once

#if __has_include(<optional>)
#   include <optional>
#   define HAS_STD_OPTIONAL
#elif __has_include(<experimental/optional>)
#   include <experimental/optional>
#   define HAS_STD_EXPERIMENTAL_OPTIONAL
#else
#   error Must have an optional type, either from <optional> or if not supported from <experimental/optional>.
#endif

#if defined HAS_STD_OPTIONAL

namespace opt {
    template<class T>
    using optional = std::optional<T>;
    using bad_optional_access = std::bad_optional_access;
    using nullopt_t = std::nullopt_t;
    using in_place_t = std::in_place_t;

    constexpr auto nullopt = std::nullopt;
    constexpr auto in_place = std::in_place;

    template<class T>
    constexpr auto make_optional(T && value)
    {
        return std::make_optional(std::forward<T>(value));
    }
}

#elif defined HAS_STD_EXPERIMENTAL_OPTIONAL

namespace opt {
    template<class T>
    using optional = std::experimental::optional<T>;
    using bad_optional_access = std::experimental::bad_optional_access;
    using nullopt_t = std::experimental::nullopt_t;
    using in_place_t = std::experimental::in_place_t;

    constexpr auto nullopt = std::experimental::nullopt;
    constexpr auto in_place = std::experimental::in_place;

    template<class T>
    constexpr auto make_optional(T && value)
    {
        return std::experimental::make_optional(std::forward<T>(value));
    }
}

#endif
\$\endgroup\$
13
\$\begingroup\$

A modification of @Jerry's answer:

#pragma once

#if __has_include(<optional>)

#   include <optional>
namespace stdx {
  using namespace ::std;
}
#elif __has_include(<experimental/optional>)
#   include <experimental/optional>
namespace stdx {
  using namespace ::std;
  using namespace ::std::experimental;
}

#else
#   error <experimental/optional> and <optional> not found
#endif

this has the advantage that the same namespace can be used for all "experimental" features (stdx).

So stdx::optional and stdx::variant both work.

We have to only using namespace ::std::experimental; after at least one <experimental/ header is included, as if it does not exist that is ill formed, and introducing one ourselves also makes the program ill formed.

\$\endgroup\$
  • \$\begingroup\$ Why have using namespace ::std; in the second case (with <experimental/optional>)? Also, why do you prefix the namespace with ::? It doesn't seem necessary to me. \$\endgroup\$ – Justin Jul 30 '16 at 2:09
  • 4
    \$\begingroup\$ @justin Because I want to minimize differences between cases where experimental/optional exists and does not. Why prefix with ::? In case there is a namespace std::std or a std::experimental::std or whatever: I want to be unambiguous. Otherwise using namespace std; after a using namespace std; might use std::std, which is unexpected. We are doing strange mojo, I am going to be paranoid. \$\endgroup\$ – Yakk Jul 30 '16 at 2:21
10
\$\begingroup\$

Although there are definitely (at times annoying) limitations on what they can do, at first glance it looks like they don't apply here so I'd at least attempt to use a namespace alias:

#pragma once

#if __has_include(<optional>)

#   include <optional>
    using namespace opt = std;

#elif __has_include(<experimental/optional>)
#   include <experimental/optional>

    using namespace opt = std::experimental;

#else
#   error Must have an optional type, either from <optional> or if not supported from <experimental/optional>.
#endif

Then from that point, you just use: opt::whatever, and that maps to either std::whatever or std::experimental::whatever, as appropriate.

\$\endgroup\$
  • \$\begingroup\$ *facepalm*. This is why I need Code Review \$\endgroup\$ – Justin Jul 29 '16 at 23:59
  • \$\begingroup\$ Wouldn't this cause problems if I try to use other experimental types than std::optional? It seems a bit weird to do opt::any... I guess then the namespace should really be stdext or something of that sort. \$\endgroup\$ – Justin Jul 30 '16 at 0:02
  • \$\begingroup\$ @Justin: Yeah, as you can probably guess from how I originally posted this, I started out with using namespace stdex = ..., then (partially) switched it to use opt as you had in the question. You could perfectly well define both stdext (or whatever) and opt, and use them as appropriate (even though they're essentially synonymous with each other). Not really sure whether the latter is a good idea or not though. \$\endgroup\$ – Jerry Coffin Jul 30 '16 at 0:07
  • 1
    \$\begingroup\$ namespace stdx { using namespace ::std; using namespace ::std::experimental; } would work, no? \$\endgroup\$ – Yakk Jul 30 '16 at 1:27
  • \$\begingroup\$ @Yakk That would require that both std and std::experimental are defined, but I don't see why that wouldn't work: namespace std { namespace experimental {} } namespace stdx { using namespace std; using namespace std::experimental; }. Wonder if there are any collisions between std and std::experimental. \$\endgroup\$ – Justin Jul 30 '16 at 1:54
5
\$\begingroup\$

Similar to the other answers, but just for optional, without bringing in the rest of std::experimental. Incidentally, we use this in a real codebase:

#pragma once

#if __has_include(<optional>)

#include <optional>
namespace stdx {
    using std::optional;
    using std::nullopt_t;
    using std::in_place_t;
    using std::bad_optional_access;
    using std::nullopt;
    using std::in_place;
    using std::make_optional;
}

#elif __has_include(<experimental/optional>)

#include <experimental/optional>
namespace stdx {
    using std::experimental::optional;
    using std::experimental::nullopt_t;
    using std::experimental::in_place_t;
    using std::experimental::bad_optional_access;
    using std::experimental::nullopt;
    using std::experimental::in_place;
    using std::experimental::make_optional;
}

#else
    #error "an implementation of optional is required!"
#endif
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.