Doc. no:  N4044 
Date:     2014-05-24
Revises:  N3603
Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>

A Three-Class IP Address Proposal, Revision 1

Table of Contents

1. Overview
2. Motivation and Scope
2.1. Scope
2.2. Target Audience
2.3. Reference Implementation
3. Impact On the Standard
4. Design Decisions
4.1. Overloads of hton and ntoh
4.2. Specialisation of less<>
5. Further Work
5.1. Address Iteration
6. Proposed Text
6.1. Definitions
6.1.1. host byte order
6.1.2. network byte order
6.2. Error reporting
6.3. Internet protocol addresses
6.3.1. Header <experimental/net> synopsis
6.3.2. Class ip::address
6.3.2.1. ip::address constructors
6.3.2.2. ip::address assignment
6.3.2.3. ip::address members
6.3.2.4. ip::address comparisons
6.3.2.5. ip::address creation
6.3.2.6. ip::address I/O
6.3.3. Class ip::address_v4
6.3.3.1. Struct ip::address_v4::bytes_type
6.3.3.2. ip::address_v4 constructors
6.3.3.3. ip::address_v4 assignment
6.3.3.4. ip::address_v4 members
6.3.3.5. ip::address_v4 static members
6.3.3.6. ip::address_v4 comparisons
6.3.3.7. ip::address_v4 creation
6.3.3.8. ip::address_v4 I/O
6.3.4. Class ip::address_v6
6.3.4.1. Struct ip::address_v6::bytes_type
6.3.4.2. ip::address_v6 constructors
6.3.4.3. ip::address_v6 assignment
6.3.4.4. ip::address_v6 members
6.3.4.5. ip::address_v6 static members
6.3.4.6. ip::address_v6 comparisons
6.3.4.7. ip::address_v6 creation
6.3.4.8. ip::address_v6 I/O
6.3.5. Class ip::bad_address_cast
6.3.6. Function ip::address_cast
6.3.7. Hash support
6.3.8. Suffixes for address literals
7. Changes in This Revision
7.1. Namespace
7.2. A Version-Independent Vocabulary Type
7.3. Use of noexcept
7.4. Explicit Creation from Strings
7.5. String Literals
7.6. Strongly-Typed Byte Representations
7.7. Use of constexpr
7.8. Make Default-Constructed address Invalid
7.9. Removed address_v4::netmask
8. Acknowledgements
9. References

1. Overview

This proposal describes a three-class design for IP address classes:

  • A vocabulary type, ip::address, for use in IP version independent code.
  • An IPv4-specific type ip::address_v4.
  • An IPv6-specific type ip::address_v6.

2. Motivation and Scope

2.1. Scope

This proposal describes only types necessary to support the manipulation of IP (Internet Protocol) addresses. Other networking facilities, such as sockets and name resolution, are outside the scope of this proposal.

2.2. Target Audience

The proposed interface is primarily intended for use by developers writing programs with a networking component, and in particular programs that utilise internet protocols such as TCP and UDP.

2.3. Reference Implementation

This proposal is based on the IP address classes in the Boost.Asio library. The proposed text was originally from N2175 "Networking Library Proposal for TR2 (Revision 1)" with small modifications to reflect enhancements made to Boost.Asio since 2007.

In this revision, the proposed text has been updated to reflect feedback and discussion from the Bristol 2013 meeting of SG4. A reference implementation of the proposal is available from GitHub.

3. Impact On the Standard

This is a pure library proposal. It does not add any new language features, nor does it alter any existing standard library headers.

This library can be implemented using compilers that conform to the C++11 standard. An implementation of this library requires operating system-specific functions that lie outside the C++11 standard.

4. Design Decisions

For more information on the motivating design decisions for the three-class design, please consult N3603.

In this section we list some of the additional proposed changes that were considered but rejected. The changes incorporated since N3603 are described at the end.

4.1. Overloads of hton and ntoh

One suggestion was that the proposal should include overloads of hton and ntoh for the address types. The motivating use case was that address objects may be used directly as part of standard-layout aggregates that are ultimately intended for network transmission.

This change was rejected on the basis that the applying hton to a class type (as opposed to a simple integer) may result in an object that is in an invalid or nonsensical state. As a matter of style, the author feels that the address types should be treated as abstractions without necessarily forcing a particular internal implementation.

The addresses' to_bytes() member functions can already be used to obtain a representation of the address in network byte order. To further aid the motivating use case, the bytes_type types have been changed to be unique types.

4.2. Specialisation of less<>

Another suggestion was that the address types should specialise std::less<> rather than provide operator< and friends. This change was rejected since it would appear to render the address types incompatible with other standard library types, such as std::tuple<>, which apply operator< to their elements.

5. Further Work

5.1. Address Iteration

It has been suggested in the SG4 discussion forum that the address classes include the ability to determine the predecessor or successor of an address. By this we mean that given an address 1.2.3.4, the predecessor address is 1.2.3.3 and the successor is 1.2.3.5.

The author feels that this feature is not a core responsibility of the address classes, but if it is desired it can instead be supported by a richer range-based interface. The following is a sketch of this approach:

class address_iterator_v4
{
public:
  typedef std::ptrdiff_t difference_type;
  typedef address_v4 value_type;
  typedef const address_v4* pointer;
  typedef const address_v4& reference;
  typedef std::bidirectional_iterator_tag iterator_category;

  explicit address_iterator_v4(const address_v4& addr);

  const address_v4& operator*() const;
  const address_v4* operator->() const;

  address_iterator_v4& operator++();
  address_iterator_v4 operator++(int);

  address_iterator_v4& operator--();
  address_iterator_v4 operator--(int);

  friend bool operator==(const address_iterator_v4& a, const address_iterator_v4& b);
  friend bool operator!=(const address_iterator_v4& a, const address_iterator_v4& b);

private:
  // ...
};

class address_range_v4
{
public:
  address_range_v4();
  explicit address_range_v4(const address_v4& addr);
  explicit address_range_v4(const address_v4& addr, const address_v4& mask);

  address_v4 network() const;
  address_v4 netmask() const;
  address_v4 broadcast() const;

  typedef address_iterator_v4 iterator;
  iterator begin() const;
  iterator end() const;
  iterator find(const address_v4& addr) const;

private:
  // ...
};

This may be used as in the following example:

address_range_v4 range(
    make_address_v4("192.168.1.0"),
    make_address_v4("255.255.255.0"));

for (auto a: range)
  std::cout << a << std::endl;

std::cout << "---" << std::endl;

std::for_each(
    range.find(make_address_v4("192.168.1.250")),
    range.end(),
    [](address_v4 a) { std::cout << a << std::endl; });

If SG4 determines that this feature is desirable, it may be added to this proposal or developed as a separate proposal. The author favours the latter.

6. Proposed Text

6.1. Definitions

6.1.1. host byte order

See section 1.4 of N3783 Network Byte Order Conversion.

6.1.2. network byte order

See section 1.4 of N3783 Network Byte Order Conversion.

6.2. Error reporting

Network library functions often provide two overloads, one that throws an exception to report system errors, and another that sets an error_code.

[Note: This supports two common use cases:

— Uses where system errors are truly exceptional and indicate a serious failure. Throwing an exception is the most appropriate response. This is the preferred default for most everyday programming.

— Uses where system errors are routine and do not necessarily represent failure. Returning an error code is the most appropriate response. This allows application specific error handling, including simply ignoring the error.

end note]

Functions not having an argument of type error_code& report errors as follows, unless otherwise specified:

— When a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, an exception of type system_error shall be thrown.

— Failure to allocate storage is reported by throwing an exception as described in the C++ standard (C++14 [res.on.exception.handling]).

— Destructors throw nothing.

Functions having an argument of type error_code& report errors as follows, unless otherwise specified:

— If a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, the error_code& argument is set as appropriate for the specific error. Otherwise, clear() is called on the error_code& argument.

6.3. Internet protocol addresses

This clause describes components that C++ programs may use to manipulate IP addresses.

6.3.1. Header <experimental/net> synopsis

namespace std {
  namespace experimental {
    namespace net {

      // Internet protocol addresses:

      namespace ip {

        struct v4_mapped_t {};
        constexpr v4_mapped_t v4_mapped;

        class address;
        class address_v4;
        class address_v6;

        class bad_address_cast;

        // address comparisons:
        bool operator==(const address&, const address&) noexcept;
        bool operator!=(const address&, const address&) noexcept;
        bool operator< (const address&, const address&) noexcept;
        bool operator> (const address&, const address&) noexcept;
        bool operator<=(const address&, const address&) noexcept;
        bool operator>=(const address&, const address&) noexcept;

        // address_v4 comparisons:
        bool operator==(const address_v4&, const address_v4&) noexcept;
        bool operator!=(const address_v4&, const address_v4&) noexcept;
        bool operator< (const address_v4&, const address_v4&) noexcept;
        bool operator> (const address_v4&, const address_v4&) noexcept;
        bool operator<=(const address_v4&, const address_v4&) noexcept;
        bool operator>=(const address_v4&, const address_v4&) noexcept;

        // address_v6 comparisons:
        bool operator==(const address_v6&, const address_v6&) noexcept;
        bool operator!=(const address_v6&, const address_v6&) noexcept;
        bool operator< (const address_v6&, const address_v6&) noexcept;
        bool operator> (const address_v6&, const address_v6&) noexcept;
        bool operator<=(const address_v6&, const address_v6&) noexcept;
        bool operator>=(const address_v6&, const address_v6&) noexcept;

        // address creation:
        address make_address(const char*);
        address make_address(const char*, error_code&) noexcept;
        address make_address(const string&);
        address make_address(const string&, error_code&) noexcept;

        // address_v4 creation:
        constexpr address_v4 make_address_v4(const address_v4::bytes_type&);
        constexpr address_v4 make_address_v4(unsigned long);
        constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6&);
        address_v4 make_address_v4(const char*);
        address_v4 make_address_v4(const char*, error_code&) noexcept;
        address_v4 make_address_v4(const string&);
        address_v4 make_address_v4(const string&, error_code&) noexcept;

        // address_v6 creation:
        constexpr address_v6 make_address_v6(const address_v6::bytes_type&, unsigned long = 0);
        constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4&) noexcept;
        address_v6 make_address_v6(const char*);
        address_v6 make_address_v6(const char*, error_code&) noexcept;
        address_v6 make_address_v6(const string&);
        address_v6 make_address_v6(const string&, error_code&) noexcept;

        // address I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>&, const address&);

        // address_v4 I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>&, const address_v4&);

        // address_v6 I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>&, const address_v6&);

        // address conversions:
        template <class T> constexpr T address_cast(const address&) noexcept(see below);
        template <class T> constexpr T address_cast(const address_v4&) noexcept(see below);
        template <class T> constexpr T address_cast(const address_v6&) noexcept(see below);

      } // namespace ip
    } // namespace net

    // hash support
    template <class T> struct hash;
    template <> struct hash<net::ip::address>;
    template <> struct hash<net::ip::address_v4>;
    template <> struct hash<net::ip::address_v6>;

    inline namespace literals {
      inline namespace net_literals {

        // suffixes for address literals
        net::ip::address operator "" ip(const char*, size_t);
        net::ip::address_v4 operator "" ipv4(const char*, size_t);
        net::ip::address_v6 operator "" ipv6(const char*, size_t);

      } // namespace net_literals
    } // namespace literals
  } // namespace experimental
} // namespace std

6.3.2. Class ip::address

namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        class address
        {
        public:
          // constructors:
          constexpr address() noexcept;
          constexpr address(const address& a) noexcept;
          template <class T> constexpr address(const T& t) noexcept(see below);
          template <class... T> explicit constexpr address(T&&... t);

          // assignment:
          address& operator=(const address& a) noexcept;

          // members:
          constexpr bool is_unspecified() const noexcept;
          constexpr bool is_loopback() const noexcept;
          constexpr bool is_multicast() const noexcept;
          constexpr bool is_v4() const noexcept;
          constexpr bool is_v6() const noexcept;
          string to_string() const;
          string to_string(error_code& ec) const;
        };

        // address comparisons:
        bool operator==(const address& a, const address& b) noexcept;
        bool operator!=(const address& a, const address& b) noexcept;
        bool operator< (const address& a, const address& b) noexcept;
        bool operator> (const address& a, const address& b) noexcept;
        bool operator<=(const address& a, const address& b) noexcept;
        bool operator>=(const address& a, const address& b) noexcept;

        // address creation:
        address make_address(const char* str);
        address make_address(const char* str, error_code& ec) noexcept;
        address make_address(const string& str);
        address make_address(const string& str, error_code& ec) noexcept;

        // address I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>& os, const address& addr);

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std
6.3.2.1. ip::address constructors
constexpr address() noexcept;

Effects: Constructs an object of class address.

Postconditions: The postconditions of this function are indicated in the table below.

Table 1. address::address() effects

expression

value

is_v4()

false

is_v6()

false


constexpr address(const address& a) noexcept;

Effects: Constructs an object of class address.

Postconditions: *this == a

template <class T> constexpr address(const T& t) noexcept(see below);

Remarks: This constructor shall not participate in overload resolution unless is_same<T, address>::value is false, and the expression address_cast<address>(t) is valid and yields an rvalue of type address. The expression inside noexcept shall be equivalent to noexcept(address_cast<address>(t)).

Effects: Constructs an object of type address with the result of the expression address_cast<address>(t).

Throws: Nothing unless the expression address_cast<address>(t) throws an exception.

template <class... T> explicit constexpr address(T&&... t);

Remarks: This constructor shall not participate in overload resolution unless the expression make_address(forward<T>(t)...) is valid and yields an rvalue of type address.

Effects: Constructs an object of type address with the result of the expression make_address(forward<T>(t)...).

6.3.2.2. ip::address assignment
address& operator=(const address& a) noexcept;

Postconditions: *this == a

Returns: *this

6.3.2.3. ip::address members
constexpr bool is_unspecified() const noexcept;

Returns: If is_v4() == true, returns address_cast<address_v4>(*this).is_unspecified(). If is_v6() == true, returns address_cast<address_v6>(*this).is_unspecified(). Otherwise returns false.

constexpr bool is_loopback() const noexcept;

Returns: If is_v4() == true, returns address_cast<address_v4>(*this).is_loopback(). If is_v6() == true, returns address_cast<address_v6>(*this).is_loopback(). Otherwise returns false.

constexpr bool is_multicast() const noexcept;

Returns: If is_v4() == true, returns address_cast<address_v4>(*this).is_multicast(). If is_v6() == true, returns address_cast<address_v6>(*this).is_multicast(). Otherwise returns false.

constexpr bool is_v4() const noexcept;

Returns: true if the object contains an IP version 4 address.

constexpr bool is_v6() const noexcept;

Returns: true if the object contains an IP version 6 address.

string to_string() const;
string to_string(error_code& ec) const;

Returns: If is_v4() == true, returns address_cast<address_v4>(*this).to_string(ec). If is_v6() == true, returns address_cast<address_v6>(*this).to_string(ec). Otherwise throws bad_address_cast.

6.3.2.4. ip::address comparisons
bool operator==(const address& a, const address& b) noexcept;

Returns: a_v4 == b_v4 && a_v6 == b_v6, where:
a_v4 is the value of a.is_v4() ? address_cast<address_v4>(a) : address_v4();
b_v4 is b.is_v4() ? address_cast<address_v4>(b) : address_v4();
a_v6 is a.is_v6() ? address_cast<address_v6>(a) : address_v6(); and
b_v6 is b.is_v6() ? address_cast<address_v6>(b) : address_v6().

bool operator!=(const address& a, const address& b) noexcept;

Returns: !(a == b).

bool operator< (const address& a, const address& b) noexcept;

Returns: a_v6 < b_v6 || a_v6 == b_v6 && a_v4 < b_v4, where:
a_v4 is the value of a.is_v4() ? address_cast<address_v4>(a) : address_v4();
b_v4 is b.is_v4() ? address_cast<address_v4>(b) : address_v4();
a_v6 is a.is_v6() ? address_cast<address_v6>(a) : address_v6(); and
b_v6 is b.is_v6() ? address_cast<address_v6>(b) : address_v6().

bool operator> (const address& a, const address& b) noexcept;

Returns: b < a.

bool operator<=(const address& a, const address& b) noexcept;

Returns: !(b < a).

bool operator>=(const address& a, const address& b) noexcept;

Returns: !(a < b).

6.3.2.5. ip::address creation
address make_address(const char* str);
address make_address(const char* str, error_code& ec) noexcept;
address make_address(const string& str);
address make_address(const string& str, error_code& ec) noexcept;

Effects: Converts a string representation of an address into an object of class address, as if by calling:

address a;
address_v6 v6a = make_address_v6(str, ec);
if (!ec)
  a = v6a;
else
{
  address_v4 v4a = make_address_v4(str, ec);
  if (!ec)
    a = v4a;
}

Returns: a.

6.3.2.6. ip::address I/O
template<class CharT, class Traits>
  basic_ostream<CharT, Traits>& operator<<(
    basic_ostream<CharT, Traits>& os, const address& addr);

Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

error_code ec;
string s = addr.to_string(ec);
if (ec)
{
  if (os.exceptions() & ios_base::failbit)
    throw system_error(ec);
  else
    os.setstate(ios_base::failbit);
}
else
  for (string::iterator i = s.begin(); i != s.end(); ++i)
    os << os.widen(*i);

Returns: os.

6.3.3. Class ip::address_v4

namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        class address_v4
        {
        public:
          // types:
          struct bytes_type;

          // constructors:
          constexpr address_v4() noexcept;
          constexpr address_v4(const address_v4& a) noexcept;
          constexpr address_v4(const bytes_type& bytes);
          template <class... T> explicit constexpr address(T&&... t);

          // assignment:
          address_v4& operator=(const address_v4& a) noexcept;

          // members:
          constexpr bool is_unspecified() const noexcept;
          constexpr bool is_loopback() const noexcept;
          constexpr bool is_class_a() const noexcept;
          constexpr bool is_class_b() const noexcept;
          constexpr bool is_class_c() const noexcept;
          constexpr bool is_multicast() const noexcept;
          constexpr bytes_type to_bytes() const noexcept;
          constexpr unsigned long to_ulong() const noexcept;
          string to_string() const;
          string to_string(error_code& ec) const;

          // static members:
          static constexpr address_v4 any() noexcept;
          static constexpr address_v4 loopback() noexcept;
          static constexpr address_v4 broadcast() noexcept;
          static constexpr address_v4 broadcast(const address_v4& addr,
            const address_v4& mask) noexcept;
        };

        // address_v4 comparisons:
        bool operator==(const address_v4& a, const address_v4& b) noexcept;
        bool operator!=(const address_v4& a, const address_v4& b) noexcept;
        bool operator< (const address_v4& a, const address_v4& b) noexcept;
        bool operator> (const address_v4& a, const address_v4& b) noexcept;
        bool operator<=(const address_v4& a, const address_v4& b) noexcept;
        bool operator>=(const address_v4& a, const address_v4& b) noexcept;

        // address_v4 creation:
        constexpr address_v4 make_address_v4(const address_v4::bytes_type& bytes);
        constexpr address_v4 make_address_v4(unsigned long val);
        constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& a);
        address_v4 make_address_v4(const char* str);
        address_v4 make_address_v4(const char* str, error_code& ec) noexcept;
        address_v4 make_address_v4(const string& str);
        address_v4 make_address_v4(const string& str, error_code& ec) noexcept;

        // address_v4 I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>& os, const address_v4& addr);

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std
6.3.3.1. Struct ip::address_v4::bytes_type
namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        struct address_v4::bytes_type : array<unsigned char, 4>
        {
          template <class... T> explicit constexpr bytes_type(T... t)
            : array<unsigned char, 4>{{static_cast<unsigned char>(t)...}} {}
        };

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std

The ip::address_v4::bytes_type type is a standard-layout struct that provides a byte-level representation of an IPv4 address in network byte order.

6.3.3.2. ip::address_v4 constructors
constexpr address_v4() noexcept;

Effects: Constructs an object of class address_v4.

Postconditions: The postconditions of this function are indicated in the table below.

Table 2. address_v4::address_v4() effects

expression

value

to_bytes()

{0, 0, 0, 0}

to_ulong()

0


constexpr address_v4(const address_v4& a) noexcept;

Effects: Constructs an object of class address_v4.

Postconditions: *this == a

constexpr address_v4(const bytes_type& bytes);

Requires: Each element of bytes is in the range [0, 0xFF].

Throws: out_of_range if any element of bytes is not in the range [0, 0xFF]. [Note: For implementations where UCHAR_MAX == 0xFF, no out-of-range detection is needed. —end note]

Postconditions: to_bytes() == bytes and to_ulong() == (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3].

template <class... T> explicit constexpr address_v4(T&&... t);

Remarks: This constructor shall not participate in overload resolution unless the expression make_address_v4(forward<T>(t)...) is valid and yields an rvalue of type address_v4.

Effects: Constructs an object of type address_v4 with the result of the expression make_address_v4(forward<T>(t)...).

6.3.3.3. ip::address_v4 assignment
address_v4& operator=(const address_v4& a) noexcept;

Postconditions: *this == a

Returns: *this

6.3.3.4. ip::address_v4 members
constexpr bool is_unspecified() const noexcept;

Returns: to_ulong() == 0.

constexpr bool is_loopback() const noexcept;

Returns: (to_ulong() & 0xFF000000) == 0x7F000000.

constexpr bool is_class_a() const noexcept;

Returns: (to_ulong() & 0x80000000) == 0.

constexpr bool is_class_b() const noexcept;

Returns: (to_ulong() & 0xC0000000) == 0x80000000.

constexpr bool is_class_c() const noexcept;

Returns: (to_ulong() & 0xE0000000) == 0xC0000000.

constexpr bool is_multicast() const noexcept;

Returns: (to_ulong() & 0xF0000000) == 0xE0000000.

constexpr bytes_type to_bytes() const noexcept;

Returns: A representation of the address in network byte order.

constexpr unsigned long to_ulong() const noexcept;

Returns: A representation of the address in host byte order.

string to_string() const;
string to_string(error_code& ec) const;

Effects: Converts an address into a string representation, as if by POSIX inet_ntop() when invoked with address family AF_INET.

Returns: If successful, the string representation of the address. Otherwise string().

6.3.3.5. ip::address_v4 static members
static constexpr address_v4 any() noexcept;

Returns: address_v4().

static constexpr address_v4 loopback() noexcept;

Returns: address_v4(0x7F000001).

static constexpr address_v4 broadcast() noexcept;

Returns: address_v4(0xFFFFFFFF).

static constexpr address_v4 broadcast(const address_v4& addr, const address_v4& mask) noexcept;

Returns: address_v4(addr.to_ulong() | ~mask.to_ulong()).

6.3.3.6. ip::address_v4 comparisons
bool operator==(const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() == b.to_ulong().

bool operator!=(const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() != b.to_ulong().

bool operator< (const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() < b.to_ulong().

bool operator> (const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() > b.to_ulong().

bool operator<=(const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() <= b.to_ulong().

bool operator>=(const address_v4& a, const address_v4& b) noexcept;

Returns: a.to_ulong() >= b.to_ulong().

6.3.3.7. ip::address_v4 creation
constexpr address_v4 make_address_v4(const address_v4::bytes_type& bytes);

Returns: address_v4(bytes).

constexpr address_v4 make_address_v4(unsigned long val);

Requires: val is in the range [0, 0xFFFFFFFF].

Throws: out_of_range if val is not in the range [0, 0xFFFFFFFF]. [Note: For implementations where ULONG_MAX == 0xFFFFFFFF, no out-of-range detection is needed. —end note]

Returns: An object a of type address_v4 where a.to_ulong() == val and a.to_bytes() is { (val >> 24) & 0xFF, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF }.

constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& a);

Requires: a.is_v4_mapped().

Returns: An address_v4 object corresponding to the IPv4-mapped IPv6 address, as if computed by the following method:

bytes_type v6b = a.to_bytes();
address_v4::bytes_type v4b(v6b[12], v6b[13], v6b[14], v6b[15]);
return address_v4(v4b);

Throws: bad_address_cast if a.is_v4_mapped() is false.

address_v4 make_address_v4(const char* str);
address_v4 make_address_v4(const char* str, error_code& ec) noexcept;
address_v4 make_address_v4(const string& str);
address_v4 make_address_v4(const string& str, error_code& ec) noexcept;

Effects: Converts a string representation of an address into a corresponding address_v4 value, as if by POSIX inet_pton() when invoked with address family AF_INET.

Returns: If successful, an address_v4 value corresponding to the string str. Otherwise address_v4().

6.3.3.8. ip::address_v4 I/O
template<class CharT, class Traits>
  basic_ostream<CharT, Traits>& operator<<(
    basic_ostream<CharT, Traits>& os, const address_v4& addr);

Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

error_code ec;
string s = addr.to_string(ec);
if (ec)
{
  if (os.exceptions() & ios_base::failbit)
    throw system_error(ec);
  else
    os.setstate(ios_base::failbit);
}
else
  for (string::iterator i = s.begin(); i != s.end(); ++i)
    os << os.widen(*i);

Returns: os.

6.3.4. Class ip::address_v6

namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        class address_v6
        {
        public:
          // types:
          struct bytes_type;

          // constructors:
          constexpr address_v6() noexcept;
          constexpr address_v6(const address_v6& a) noexcept;
          constexpr address_v6(const bytes_type& bytes, unsigned long scope);
          template <class... T> explicit constexpr address(T&&... t);

          // assignment:
          address_v4& operator=(const address_v4& a) noexcept;

          // members:
          void scope_id(unsigned long id) noexcept;
          constexpr unsigned long scope_id() const noexcept;
          constexpr bool is_unspecified() const noexcept;
          constexpr bool is_loopback() const noexcept;
          constexpr bool is_multicast() const noexcept;
          constexpr bool is_link_local() const noexcept;
          constexpr bool is_site_local() const noexcept;
          constexpr bool is_v4_mapped() const noexcept;
          constexpr bool is_multicast_node_local() const noexcept;
          constexpr bool is_multicast_link_local() const noexcept;
          constexpr bool is_multicast_site_local() const noexcept;
          constexpr bool is_multicast_org_local() const noexcept;
          constexpr bool is_multicast_global() const noexcept;
          constexpr bytes_type to_bytes() const noexcept;
          string to_string() const;
          string to_string(error_code& ec) const;

          // static members:
          static constexpr address_v6 any() noexcept;
          static constexpr address_v6 loopback() noexcept;
        };

        // address_v6 comparisons:
        bool operator==(const address_v6& a, const address_v6& b) noexcept;
        bool operator!=(const address_v6& a, const address_v6& b) noexcept;
        bool operator< (const address_v6& a, const address_v6& b) noexcept;
        bool operator> (const address_v6& a, const address_v6& b) noexcept;
        bool operator<=(const address_v6& a, const address_v6& b) noexcept;
        bool operator>=(const address_v6& a, const address_v6& b) noexcept;

        // address_v6 creation:
        constexpr address_v6 make_address_v6(const address_v6::bytes_type& bytes,
                                             unsigned long scope_id = 0);
        constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4& a) noexcept;
        address_v6 make_address_v6(const char* str);
        address_v6 make_address_v6(const char* str, error_code& ec) noexcept;
        address_v6 make_address_v6(const string& str);
        address_v6 make_address_v6(const string& str, error_code& ec) noexcept;

        // address_v6 I/O:
        template<class CharT, class Traits>
          basic_ostream<CharT, Traits>& operator<<(
            basic_ostream<CharT, Traits>& os, const address_v6& addr);

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std

[Note: The implementations of the functions is_unspecified, is_loopback, is_multicast, is_link_local, is_site_local, is_v4_mapped, is_multicast_node_local, is_multicast_link_local, is_multicast_site_local, is_multicast_org_local and is_multicast_global are determined by [RFC4291]. —end note]

6.3.4.1. Struct ip::address_v6::bytes_type
namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        struct address_v6::bytes_type : array<unsigned char, 16>
        {
          template <class... T> explicit constexpr bytes_type(T... t)
            : array<unsigned char, 16>{{static_cast<unsigned char>(t)...}} {}
        };

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std

The ip::address_v6::bytes_type type is a standard-layout struct that provides a byte-level representation of an IPv6 address in network byte order.

6.3.4.2. ip::address_v6 constructors
constexpr address_v6() noexcept;

Effects: Constructs an object of class address_v6.

Postconditions: The postconditions of this function are indicated in the table below.

Table 3. address_v6::address_v6() effects

expression

value

is_unspecified()

true

scope_id()

0


constexpr address_v6(const address_v6& a) noexcept;

Effects: Constructs an object of class address_v6.

Postconditions: *this == a

constexpr address_v6(const bytes_type& bytes, unsigned long scope);

Requires: Each element of bytes is in the range [0, 0xFF].

Throws: out_of_range if any element of bytes is not in the range [0, 0xFF]. [Note: For implementations where UCHAR_MAX == 0xFF, no out-of-range detection is needed. —end note]

Postconditions: to_bytes() == bytes and scope_id() == scope.

template <class... T> explicit constexpr address_v6(T&&... t);

Remarks: This constructor shall not participate in overload resolution unless the expression make_address_v6(forward<T>(t)...) is valid and yields an rvalue of type address_v6.

Effects: Constructs an object of type address_v6 with the result of the expression make_address_v6(forward<T>(t)...).

6.3.4.3. ip::address_v6 assignment
address_v6& operator=(const address_v6& a) noexcept;

Postconditions: *this == a

Returns: *this

6.3.4.4. ip::address_v6 members
void scope_id(unsigned long id) noexcept;

Postconditions: scope_id() == id.

constexpr unsigned long scope_id() const noexcept;

Returns: The scope identifier associated with the address.

constexpr bool is_unspecified() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents an unspecified address, as if computed by the following method:

bytes_type b = to_bytes();
return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0 && b[ 3] == 0
    && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0 && b[ 7] == 0
    && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0 && b[11] == 0
    && b[12] == 0 && b[13] == 0 && b[14] == 0 && b[15] == 0;

constexpr bool is_loopback() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a loopback address, as if computed by the following method:

bytes_type b = to_bytes();
return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0 && b[ 3] == 0
    && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0 && b[ 7] == 0
    && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0 && b[11] == 0
    && b[12] == 0 && b[13] == 0 && b[14] == 0 && b[15] == 1;

constexpr bool is_multicast() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF;

constexpr bool is_link_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a unicast link-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFE && (b[1] & 0xC0) == 0x80;

constexpr bool is_site_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a unicast site-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFE && (b[1] & 0xC0) == 0xC0;

constexpr bool is_v4_mapped() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents an IPv4-mapped IPv6 address, as if computed by the following method:

bytes_type b = to_bytes();
return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0    && b[ 3] == 0
    && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0    && b[ 7] == 0
    && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0xFF && b[11] == 0xFF;

constexpr bool is_multicast_node_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast node-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF && (b[1] & 0x0F) == 0x01;

constexpr bool is_multicast_link_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast link-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF && (b[1] & 0x0F) == 0x02;

constexpr bool is_multicast_site_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast site-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF && (b[1] & 0x0F) == 0x05;

constexpr bool is_multicast_org_local() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast organisation-local address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF && (b[1] & 0x0F) == 0x08;

constexpr bool is_multicast_global() const noexcept;

Returns: A boolean indicating whether the address_v6 object represents a multicast global address, as if computed by the following method:

bytes_type b = to_bytes();
return b[0] == 0xFF && (b[1] & 0x0F) == 0x0E;

constexpr bytes_type to_bytes() const noexcept;

Returns: A representation of the address in network byte order.

string to_string() const;
string to_string(error_code& ec) const;

Effects: Converts an address into a string representation. If scope_id() == 0, converts as if by POSIX inet_ntop() when invoked with address family AF_INET6. If scope_id() != 0, the format is address%scope-id, where address is the string representation of the equivalent address having scope_id() == 0, and scope-id is an implementation-defined string representation of the scope identifier.

Returns: If successful, the string representation of the address. Otherwise string().

6.3.4.5. ip::address_v6 static members
static constexpr address_v6 any() noexcept;

Returns: address_v6().

static constexpr address_v6 loopback() noexcept;

Returns: An address a such that the condition a.is_loopback() holds.

6.3.4.6. ip::address_v6 comparisons
bool operator==(const address_v6& a, const address_v6& b) noexcept;

Returns: a.to_bytes() == b.to_bytes() && a.scope_id() == b.scope_id().

bool operator!=(const address_v6& a, const address_v6& b) noexcept;

Returns: !(a == b).

bool operator< (const address_v6& a, const address_v6& b) noexcept;

Returns: a.to_bytes() < b.to_bytes() || (!(b.to_bytes() < a.to_bytes()) && a.scope_id() < b.scope_id()).

bool operator> (const address_v6& a, const address_v6& b) noexcept;

Returns: b < a.

bool operator<=(const address_v6& a, const address_v6& b) noexcept;

Returns: !(b < a).

bool operator>=(const address_v6& a, const address_v6& b) noexcept;

Returns: !(a < b).

6.3.4.7. ip::address_v6 creation
constexpr address_v6 make_address_v6(const address_v6::bytes_type& bytes,
                                     unsigned long scope_id);

Returns: address_v6(bytes, scope_id).

constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4& a) noexcept;

Returns: An address_v6 object containing the IPv4-mapped IPv6 address corresponding to the specified IPv4 address, as if computed by the following method:

address_v4::bytes_type v4b = a.to_bytes();
bytes_type v6b(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
               0xFF, 0xFF, v4b[0], v4b[1], v4b[2], v4b[3]);
return address_v6(v6b);

address_v6 make_address_v6(const char* str);
address_v6 make_address_v6(const char* str, error_code& ec) noexcept;
address_v6 make_address_v6(const string& str);
address_v6 make_address_v6(const string& str, error_code& ec) noexcept;

Effects: Converts a string representation of an address into a corresponding address_v6 value. The format is either address or address%scope-id, where address is in the format specified by POSIX inet_pton() when invoked with address family AF_INET6, and scope-id is an optional string specifying the scope identifier. All implementations shall accept as scope-id a string representation of an unsigned decimal integer. It is implementation-defined whether alternative scope identifier representations are permitted. If scope-id is not supplied, an address_v6 object shall be returned such that scope_id() == 0.

Returns: If successful, an address_v6 value corresponding to the string str. Otherwise returns address_v6().

6.3.4.8. ip::address_v6 I/O
template<class CharT, class Traits>
  basic_ostream<CharT, Traits>& operator<<(
    basic_ostream<CharT, Traits>& os, const address_v6& addr);

Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

error_code ec;
string s = addr.to_string(ec);
if (ec)
{
  if (os.exceptions() & ios_base::failbit)
    throw system_error(ec);
  else
    os.setstate(ios_base::failbit);
}
else
  for (string::iterator i = s.begin(); i != s.end(); ++i)
    os << os.widen(*i);

Returns: os.

6.3.5. Class ip::bad_address_cast

namespace std {
  namespace experimental {
    namespace net {
      namespace ip {

        class bad_address_cast : bad_cast
        {
        public:
          virtual const char* what() const noexcept;
        };

      } // namespace ip
    } // namespace net
  } // namespace experimental
} // namespace std

Objects of type bad_address_cast are thrown by a failed address_cast.

6.3.6. Function ip::address_cast

template <class T> constexpr T address_cast(const address& a) noexcept(see below);

This function template shall participate in overload resolution only for the types T listed in the table below.

Table 4. template <class T> constexpr T address_cast(const address&) effects

T

noexcept

remarks

address

true

Returns a.

address_v4

false

If a.is_v4() is true, returns the IP version 4 address specified in the version-independent address object a. Otherwise, throws bad_address_cast.

address_v6

false

If a.is_v6() is true, returns the IP version 6 address specified in the version-independent address object a. Otherwise, throws bad_address_cast.


template <class T> constexpr T address_cast(const address_v4& a) noexcept(see below);

This function template shall participate in overload resolution only for the types T listed in the table below.

Table 5. template <class T> constexpr T address_cast(const address_v4&) effects

T

noexcept

remarks

address

true

Returns a version-independent address object b such that b.is_v4() is true, b.is_v6() is false, and address_cast<address_v4>(b) == a.

address_v4

true

Returns a.

address_v6

Function overload is deleted.


template <class T> constexpr T address_cast(const address_v6& a) noexcept(see below);

This function template shall participate in overload resolution only for the types T listed in the table below.

Table 6. template <class T> constexpr T address_cast(const address_v6&) effects

T

noexcept

remarks

address

true

Returns a version-independent address object b such that b.is_v4() is false, b.is_v6() is true, and address_cast<address_v6>(b) == a.

address_v4

Function overload is deleted.

address_v6

true

Returns a.


6.3.7. Hash support

template <> struct hash<net::ip::address>;
template <> struct hash<net::ip::address_v4>;
template <> struct hash<net::ip::address_v6>;

Requires: the template specializations shall meet the requirements of class template hash (C++14 [unord.hash]).

6.3.8. Suffixes for address literals

This section describes literal suffixes for constructing address literals. The suffixes ip, ipv4 and ipv6 denote address values of the corresponding types ip::address, ip::address_v4 and ip::address_v6 respectively if they are applied to string literals.

[Example: The following code shows some address literals.

using namespace std::experimental::net_literals;
auto addr = "127.0.0.1"ip;
auto v4_addr = "255.255.255.255"ipv4;
auto v6_addr = "::1"ipv6;

end example]

net::ip::address operator "" ip(const char* str, size_t);

Returns: net::ip::make_address(str).

net::ip::address_v4 operator "" ipv4(const char* str, size_t);

Returns: net::ip::make_address_v4(str).

net::ip::address_v6 operator "" ipv6(const char* str, size_t);

Returns: net::ip::make_address_v6(str).

7. Changes in This Revision

In this revision of the proposal, we include changes made based on feedback and discussion from the 2013 meeting of SG4 at Bristol, where N3603 was discussed.

7.1. Namespace

This revision moves the proposed types and functions from the std:: namespace to std::experimental::.

7.2. A Version-Independent Vocabulary Type

The ip::address class has been modified so that the version-specific types do not appear in its interface. An approach based on the any class has been adopted (see Library Fundamentals working draft), and address types may be explicitly converted using the address_cast<> function. The version-specific types are implicitly convertible to ip::address via a template constructor.

For example:

address_v4 a;
address b = a; // ok, implicit conversion
address c = address_cast<address>(a); // ok, explicit conversion
address_v4 d = address_cast<address_v4>(b); // ok, explicit conversion
address_v6 e = address_cast<address_v6>(b); // invalid, throws bad_address_cast

7.3. Use of noexcept

Many functions have now been marked noexcept.

7.4. Explicit Creation from Strings

This replaces the from_string() static member functions with new free functions make_address(), make_address_v4() and make_address_v6(). These functions allow explicit creation of the address types from strings and other types, such as an array of bytes or an integer in host byte order. For example, one overload throws an exception on failure:

address_v4 a = make_address_v4("127.0.0.1"); // succeeds
address_v4 b = make_address_v4("::1"); // throws

and another overload provides a non-throwing alternative:

error_code ec;
address_v4 b = make_address_v4("127.0.0.1", ec); // succeeds, clears ec
address_v4 b = make_address_v4("::1", ec); // sets ec

An explicit template constructor has been added to also support this style of use:

address c("127.0.0.1");

This constructor simply forwards the arguments to the appropriate make_address() overload.

7.5. String Literals

Support for IP address literals has been included. For example:

using namespace std::experimental::net_literals;
auto addr = "127.0.0.1"ip;
auto v4_addr = "255.255.255.255"ipv4;
auto v6_addr = "::1"ipv6;

7.6. Strongly-Typed Byte Representations

The nested typedefs bytes_type have been changed to be unique standard-layout types. Objects of type bytes_type are implicitly convertible to the corresponding address class. This is intended to support the following use case, where the byte representations of addresses are included as part of a larger standard-layout structure, such as a packet definition:

struct header
{
  address_v4::bytes_type from;
  address_v4::bytes_type to;
  // ...
};

struct message
{
  header hdr;
  // ...
};

// ...

address_v4 old_address = "1.2.3.4"ipv4;
address_v4 new_address = "4.3.2.1"ipv4;

message m;
receive_message(m); // e.g. reads bytes from a socket
if (m.hdr.to == old_address)
  m.hdr.to = new_address.to_bytes();

7.7. Use of constexpr

The constexpr keyword has been applied to some of the functions in the proposal. The rationale for this change is that many of the equivalent POSIX macros can be used in constant expressions.

7.8. Make Default-Constructed address Invalid

A default-constructed address object now represents an invalid address that is neither IPv4 nor IPv6. Any attempt to address_cast it to another type will result in a bad_address_cast exception.

7.9. Removed address_v4::netmask

The ip::address_v4::netmask function has been removed as it is not possible to give the correct answer, given only an arbitrary IP address, without access to the routing table.

8. Acknowledgements

The author would like to thank Arash Partow, Jamie Allsop, James Anderson and Kyle Kloepper for their feedback and suggestions.

9. References

[RFC4291] Hinden, R. and Deering, S., RFC 4291: Internet Protocol Version 6 (IPv6) Addressing Architecture, 2006, http://www.ietf.org/rfc/rfc4291.txt.