Doc. no:  N3603
Date:     2013-03-17
Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>

A Three-Class IP Address Proposal

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. A Version-Independent Vocabulary Type
4.1.1. Why Type Erasure?
4.2. Version-Specific Types
4.3. Two Classes is Too Few
4.4. Namespace
5. Proposed Text
5.1. Definitions
5.1.1. host byte order
5.1.2. network byte order
5.2. Internet protocol addresses
5.2.1. Header <network> synopsis
5.2.2. Class ip::address
5.2.2.1. ip::address constructors
5.2.2.2. ip::address assignment
5.2.2.3. ip::address members
5.2.2.4. ip::address static members
5.2.2.5. ip::address comparisons
5.2.2.6. ip::address I/O
5.2.3. Class ip::address_v4
5.2.3.1. ip::address_v4 constructors
5.2.3.2. ip::address_v4 members
5.2.3.3. ip::address_v4 static members
5.2.3.4. ip::address_v4 comparisons
5.2.3.5. ip::address_v4 I/O
5.2.4. Class ip::address_v6
5.2.4.1. ip::address_v6 constructors
5.2.4.2. ip::address_v6 members
5.2.4.3. ip::address_v6 static members
5.2.4.4. ip::address_v6 comparisons
5.2.4.5. ip::address_v6 I/O
5.2.5. Hash support
6. Acknowledgements
7. 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 is from N2175 "Networking Library Proposal for TR2 (Revision 1)" with small modifications to reflect enhancements made to Boost.Asio since 2007. At present, the proposed text does not differ significantly from the corresponding classes in Boost.Asio.

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

4.1. A Version-Independent Vocabulary Type

Ideally, most network applications should be written so that they are independent of the IP version in use. For this reason, this proposal provides the ip::address class as a vocabulary type.

The characteristics of the ip::address class are as follows:

  • Uses type erasure. It may hold either an IPv4 or IPv6 address, but it preserves original type.
  • May be used with AF_INET or AF_INET6 sockets.
  • May be converted to or from either an IPv4 address string or an IPv6 address string. Automatically determines the correct underlying type.
  • May be converted to or from an IPv4 or IPv6 address object of the corresponding version-specific class. ("To" assuming the version-independent source holds the correct underlying type.)
  • Provides various operations, for testing and manipulating addresses, that apply to both IPv4 and IPv6.
  • When default-constructed, initialises the ip::address object so that it corresponds to a default-constructed IPv4 address. (This particular value was chosen in Boost.Asio as IPv6 networking support is not universal, and the value is also the smallest in terms of ip::address::operator<.)

In general, the ip::address interface is designed to provide just enough support to use IP addresses in version-independent networking contexts. When a program requires a facility that applies to only IPv4 or IPv6 addresses, the developer is expected to use one of the version-specific types described below.

4.1.1. Why Type Erasure?

The question of whether to use a type-erased vocabulary type was discussed during the 2005 Boost review of Boost.Asio. What was originally proposed was to distinguish IPv4 and IPv6 using separate types and to use template-based genericity to be version independent. This approach was rejected by the participants in the review.

The point is that most applications do indeed need to be version independent at runtime. For example:

Usage: my_tcp_server <bind_to_address>

./my_tcp_server 0.0.0.0    # Binds to IPv4 wildcard address
./my_tcp_server 127.0.0.1  # Binds to specific IPv4 address
./my_tcp_server ::0        # Binds to IPv6 wildcard address
./my_tcp_server ::1        # Binds to specific IPv6 address

The implementation of my_tcp_server only deals in one type of address class. No templates are required.

Forcing all applications to create their own runtime switching was deemed infeasible. Dual-stack support is not universal and cannot be relied on for portable applications. Hence the current form in Boost.Asio exists as a tradeoff between supporting the easy development of version-independent applications and the low level access for those who need it.

4.2. Version-Specific Types

The version-specific address classes, ip::address_v4 and ip::address_v6, are provided for use cases where finer grained control is required, such as:

  • Reduced memory consumption. An application may need to store many addresses.
  • Speed. A complex algorithm may deal with only a single type of address, and the cost of repeatedly extracting the underlying value from a type-erased type is prohibitive.
  • Compile-time type safety. A programmer may wish to provide an interface that deals in only one particular IP version. Another may wish to provide different overloads of a function that are distinguished by IP version.

The characteristics of the ip::address_v4 class are as follows:

  • Specifically an IPv4 address.
  • For use with AF_INET sockets.
  • May be converted to or from an IPv4-form address string (a.b.c.d). Rejects other string forms.
  • May be converted to or from a 32 bit integer in host byte order. (Some applications use this for low level bit manipulation.)
  • May be converted to or from a 32 bit integer / 4 byte array in network byte order. (E.g. if used for transmission when embedded within a network protocol.)
  • Provides static member functions for creating well-known IPv4 addresses.
  • Provides various other operations for testing and manipulating IPv4 addresses.

The characteristics of the ip::address_v6 class are as follows:

  • Specifically an IPv6 address.
  • For use with AF_INET6 sockets.
  • May be converted to or from an IPv6-form address string (abcd:efgh:..., etc). Rejects other string forms.
  • May be converted to or from a 128 bit integer / 16 byte array in network byte order. (E.g. again, if used for transmission.)
  • May be explicitly created from an ip::address_v4, with the creation function determining which conversion is used (to v4-mapped or v4-compatible).
  • May be explicitly converted to an ip::address_v4, if the address is one of the above v4 forms.
  • Provides static member functions for creating well-known IPv6 addresses.
  • Provides various other operations for testing and manipulating IPv6 addresses.

4.3. Two Classes is Too Few

An alternative IP address design has been proposed that revolves around two classes. Using the naming conventions of this proposal, these classes would be:

  • A vocabulary type, ip::address, for use in IP version independent code. Unlike the three-class design, this class is also intended to represent IPv6 addresses.
  • A version-specific type, ip::address_v4.

This design appears to be driven by a desire to push people toward the version-independent type, but to still meet the space constraints of applications that must deal with large numbers of IPv4 addresses. The design is based on the notion that IPv4 addresses are a subset of IPv6 addresses, and that IPv4 addresses can be mapped, and stored, into IPv6.

The author rejects this design for a number of reasons.

  • It lacks symmetry and is potentially surprising to users.

Superficially, the two-class design gives a special place to IPv4 addresses ("I can see the IPv4 class... but where's IPv6?").

What is more important, however, is that the design prevents a programmer from utilising the C++ type system to create efficient function overloads based on the IP version. A programmer can provide a function overload specifically for IPv4 addresses:

void some_function(const ip::address_v4& a)
{
  ...
}

but when it comes to supporting IPv6 addresses the programmer has no choice but to resort to runtime dispatching:

void some_function(const ip::address& a)
{
  if (a.is_v6())
  {
    ...
  }
  else
  {
    ...
  }
}

There is no way for an programmer to express an interface that wants to deal only in IPv6 addresses.

  • It unnecessarily discards information.

A pure IPv4 address is subtly different from one mapped into an IPv6 address. This ties into the question of the larger networking ecosystem, in particular how one might use the address classes in conjunction with sockets. For example:

  • "1.2.3.4" -> AF_INET socket
  • "::ffff:1.2.3.4" -> AF_INET6 with dual stack socket
  • "fe80::...:12:34" -> AF_INET6 with or without dual stack

Things like dual stack support are far from universal and some IPv6 transition/portability guides recommend explicitly disabling dual stack support. The author does not think that standardized address classes should push people in one or other direction with respect to dual stack usage. Thus, it is important to preserve the original type of the IP address.

  • It treats IPv6 operations as a superset of IPv4 operations.

As there is no separate type for IPv6, the vocabulary type ip::address must support all IPv6-related operations. Some of these operations may lack a meaningful implementation for IPv4 addresses, and so behave in quite different ways (e.g. fail). Note that some of these operations are outside the scope of standardization, i.e. they may be part of an application.

4.4. Namespace

This proposal follows Boost.Asio in the use of the namespace ip::, rather than the prefix ip_, for the address classes. This namespace is part of a wider taxonomy and encompasses other IP-related facilities, including:

  • Protocol classes (tcp, udp and icmp).
  • Socket option classes related to unicast and multicast.
  • Classes and functions related to IP name resolution.

The author notes that the abbreviation IP is an overloaded term, even within computing. If backward compatibility was not a concern, he would instead prefer the namespace inet::. This is consistent with POSIX naming, as in AF_INET, inet_ntop(), etc. It is also unambiguously network related and so might be placed directly under the std namespace, i.e. std::inet.

5. Proposed Text

5.1. Definitions

5.1.1. host byte order

See Host and Network Byte Orders.

5.1.2. network byte order

See Host and Network Byte Orders.

5.2. Internet protocol addresses

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

5.2.1. Header <network> synopsis

namespace std {
  namespace net {

    // Internet protocol addresses:

    namespace ip {

      class address;

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

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

      class address_v4;

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

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

      class address_v6;

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

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

    } // 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>;

} // namespace std

5.2.2. Class ip::address

namespace std {
  namespace net {
    namespace ip {

      class address
      {
      public:
        // constructors:
        address();
        address(const address_v4& addr);
        address(const address_v6& addr);

        // assignment:
        address& operator=(const address_v4& addr);
        address& operator=(const address_v6& addr);

        // members:
        bool is_unspecified() const;
        bool is_loopback() const;
        bool is_multicast() const;
        bool is_v4() const;
        bool is_v6() const;
        address_v4 to_v4() const;
        address_v6 to_v6() const;
        string to_string() const;
        string to_string(error_code& ec) const;

        // static members:
        static address from_string(const string& str);
        static address from_string(const string& str, error_code& ec);
      };

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

      // 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 std
5.2.2.1. ip::address constructors
address();

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()

true

is_v6()

false

to_v4()

address_v4()


address(const address_v4& addr);

Effects: Constructs an object of class address, initialising it with the specified IP version 4 address.

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

Table 2. address::address(const address_v4&) effects

expression

value

is_v4()

true

is_v6()

false

to_v4()

addr


address(const address_v6& addr);

Effects: Constructs an object of class address, initialising it with the specified IP version 6 address.

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

Table 3. address::address(const address_v6&) effects

expression

value

is_v4()

false

is_v6()

true

to_v6()

addr


5.2.2.2. ip::address assignment
address& operator=(const address_v4& addr);

Effects: Assigns the specified IP version 4 address into an object of class address.

Returns: *this.

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

Table 4. address& address::operator=(const address_v4&) effects

expression

value

is_v4()

true

is_v6()

false

to_v4()

addr


address& operator=(const address_v6& addr);

Effects: Assigns the specified IP version 6 address into an object of class address.

Returns: *this.

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

Table 5. address& address::operator=(const address_v6&) effects

expression

value

is_v4()

false

is_v6()

true

to_v6()

addr


5.2.2.3. ip::address members
bool is_unspecified() const;

Returns: is_v4() ? to_v4().is_unspecified() : to_v6().is_unspecified().

bool is_loopback() const;

Returns: is_v4() ? to_v4().is_loopback() : to_v6().is_loopback().

bool is_multicast() const;

Returns: is_v4() ? to_v4().is_multicast() : to_v6().is_multicast().

bool is_v4() const;

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

bool is_v6() const;

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

address_v4 to_v4() const;

Returns: The IP version 4 address contained by the object.

Throws: bad_cast if is_v4() is false.

address_v6 to_v6() const;

Returns: The IP version 6 address contained by the object.

Throws: bad_cast if is_v6() is false.

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

Returns: is_v4() ? to_v4().to_string(ec) : to_v6().to_string(ec).

5.2.2.4. ip::address static members
static address from_string(const string& str);
static address from_string(const string& str, error_code& ec);

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

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

Returns: a.

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

Returns: (a.is_v4() && b.is_v4() && a.to_v4() == b.to_v4()) || (a.is_v6() && b.is_v6() && a.to_v6() == b.to_v6()).

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

Returns: !(a == b).

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

Returns: (a.is_v4() && b.is_v4() && a.to_v4() < b.to_v4()) || (a.is_v6() && b.is_v6() && a.to_v6() < b.to_v6()) || (a.is_v4() && b.is_v6()).

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

Returns: b < a.

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

Returns: !(b < a).

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

Returns: !(a < b).

5.2.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.

5.2.3. Class ip::address_v4

namespace std {
  namespace net {
    namespace ip {

      class address_v4
      {
      public:
        // types:
        typedef array<unsigned char, 4> bytes_type;

        // constructors:
        address_v4();
        explicit address_v4(const bytes_type& bytes);
        explicit address_v4(unsigned long val);

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

        // static members:
        static address_v4 from_string(const string& str);
        static address_v4 from_string(const string& str, error_code& ec);
        static address_v4 any();
        static address_v4 loopback();
        static address_v4 broadcast();
        static address_v4 broadcast(const address_v4& addr,
          const address_v4& mask);
        static address_v4 netmask(const address_v4& addr);
      };

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

      // 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 std
5.2.3.1. ip::address_v4 constructors
address_v4();

Effects: Constructs an object of class address_v4.

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

Table 6. address_v4::address_v4() effects

expression

value

to_bytes()

{0, 0, 0, 0}

to_ulong()

0


address_v4(const bytes_type& bytes);

Effects: Constructs an object of class address_v4.

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

Throws: out_of_range if any element of the array 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: The postconditions of this function are indicated in the table below.

Table 7. address_v4::address_v4(const bytes_type&) effects

expression

value

to_bytes()

{ bytes[0], bytes[1],
  bytes[2], bytes[3] }

to_ulong()

(bytes[0] << 24) | (bytes[1] << 16)
  | (bytes[2] << 8) | bytes[3]


address_v4(unsigned long val);

Effects: Constructs an object of class address_v4.

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]

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

Table 8. address_v4::address_v4(unsigned long) effects

expression

value

to_bytes()

{ (val >> 24) & 0xFF, (val >> 16) & 0xFF,
  (val >> 8)  & 0xFF, val & 0xFF }

to_ulong()

val


5.2.3.2. ip::address_v4 members
bool is_unspecified() const;

Returns: to_ulong() == 0.

bool is_loopback() const;

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

bool is_class_a() const;

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

bool is_class_b() const;

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

bool is_class_c() const;

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

bool is_multicast() const;

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

bytes_type to_bytes() const;

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

unsigned long to_ulong() const;

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().

5.2.3.3. ip::address_v4 static members
static address_v4 from_string(const string& str);
static address_v4 from_string(const string& str, error_code& ec);

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().

static address_v4 any();

Returns: address_v4().

static address_v4 loopback();

Returns: address_v4(0x7F000001).

static address_v4 broadcast();

Returns: address_v4(0xFFFFFFFF).

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

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

static address_v4 netmask(const address_v4& addr);

If addr.is_class_a() is true, returns address_v4(0xFF000000). If addr.is_class_b() is true, returns address_v4(0xFFFF0000). If addr.is_class_c() is true, returns address_v4(0xFFFFFF00). Otherwise returns address_v4(0xFFFFFFFF).

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

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

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

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

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

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

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

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

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

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

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

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

5.2.3.5. 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.

5.2.4. Class ip::address_v6

namespace std {
  namespace net {
    namespace ip {

      class address_v6
      {
      public:
        // types:
        typedef array<unsigned char, 16> bytes_type;

        // constructors:
        address_v6();
        explicit address_v6(const bytes_type& bytes,
                            unsigned long scope_id = 0);

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

        // static members:
        static address_v6 from_string(const string& str);
        static address_v6 from_string(const string& str, error_code& ec);
        static address_v6 any();
        static address_v6 loopback();
        static address_v6 v4_mapped(const address_v4& addr);
        static address_v6 v4_compatible(const address_v4& addr);
      };

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

      // 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 std

[Note: The implementations of the functions is_unspecified, is_loopback, is_multicast, is_link_local, is_site_local, is_v4_mapped, is_v4_compatible, 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]

5.2.4.1. ip::address_v6 constructors
address_v6();

Effects: Constructs an object of class address_v6.

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

Table 9. address_v6::address_v6() effects

expression

value

is_unspecified()

true

scope_id()

0


explicit address_v6(const bytes_type& bytes,
                    unsigned long scope_id = 0);

Effects: Constructs an object of class address_v6.

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

Table 10. address_v6::address_v6() effects

expression

value

to_bytes()

bytes

scope_id()

scope_id


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

Postconditions: scope_id() == id.

unsigned long scope_id() const;

Returns: The scope identifier associated with the address.

bool is_unspecified() const;

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;

bool is_loopback() const;

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;

bool is_multicast() const;

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;

bool is_link_local() const;

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;

bool is_site_local() const;

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;

bool is_v4_mapped() const;

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;

bool is_v4_compatible() const;

Returns: A boolean indicating whether the address_v6 object represents an IPv4-compatible 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] == 0 && b[11] == 0
    && !is_unspecified() && !is_loopback();

bool is_multicast_node_local() const;

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;

bool is_multicast_link_local() const;

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;

bool is_multicast_site_local() const;

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;

bool is_multicast_org_local() const;

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;

bool is_multicast_global() const;

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) == 0x0E;

bytes_type to_bytes() const;

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().

address_v4 to_v4() const;

Requires: is_v4_mapped() || is_v4_compatible().

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

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

Throws: bad_cast if is_v4_mapped() is false and is_v4_compatible() is false.

5.2.4.3. ip::address_v6 static members
static address_v6 from_string(const string& str);
static address_v6 from_string(const string& str, error_code& ec);

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().

static address_v6 any();

Returns: address_v6().

static address_v6 loopback();

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

static address_v6 v4_mapped(const address_v4& addr);

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 = addr.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);

static address_v6 v4_compatible(const address_v4& addr);

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

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

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

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

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

Returns: !(a == b).

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

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);

Returns: b < a.

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

Returns: !(b < a).

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

Returns: !(a < b).

5.2.4.5. 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.

5.2.5. 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++11 [unord.hash]).

6. Acknowledgements

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

7. References

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