For working with IP addresses, the library provides three classes:
ipv4_address — A class for working with IPv4 addresses, where the instance size is always 4 bytes.
ipv6_address — A class for working with IPv6 addresses, where the instance size includes both the space allocated for the IPv6 address itself and the scope id (zone index). As a result, the instance size will be 16 bytes plus the maximum length of the scope id (plus aligned bytes if any). Read about scope ids below.
ip_address — Combines the ipv4_address and ipv6_address classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from ipv4_address and ipv6_address. The instance size is the same as that of ipv6_address plus IP address version (and data alignment).
Note
Regardless of which class you use, the internal address is stored as an array of bytes, not as an unsigned integer. This design choice ensures that the address is stored in network byte order (big-endian) regardless of the platform. This is convenient because ultimately, this address can be used in sockets or similar libraries that require passing addresses in network byte order.
Note
All operations on addresses support compile-time computations. If an address is specified as a constant, during compilation, we get: errors if the address was entered incorrectly, the resulting IP address represented as bytes, and any other operations on addresses.
Parsing ip strings
Let's look at examples of obtaining IP addresses from string literals and strings.
Namespace dedicated to IP addresses and networks operations.
Definition base-v4.hpp:18
Remarks
For compile-time computations, the ipaddress library uses relaxed constexpr, which was introduced only in C++14. Therefore, for C++11, the ipaddress library does not support constant expressions. It is recommended to use the C++14 language standard or newer versions.
Access to specific versions of IP addresses for ip_address is demonstrated below.
// Below is how to distinguish between different versions of IP
constexprauto ip1_version = ip1.version(); // V4
constexprauto ip2_version = ip2.version(); // V6
constexprauto ip1_size = ip1.size(); // 4
constexprauto ip2_size = ip2.size(); // 16
// Check which IP version is stored in ip_address for ip2
constexprauto ip2_is_v4 = ip2.is_v4(); // false
constexprauto ip2_is_v6 = ip2.is_v6(); // true
// Access to specific versions of IP addresses for ip2
constexprauto ip2_get_ipv4 = ip2.v4();
constexprauto ip2_get_ipv6 = ip2.v6();
if (ip2_get_ipv4) { // false
constexprauto result = ip2_get_ipv4.value();
}
if (ip2_get_ipv6.has_value()) { // true
constexprauto result = ip2_get_ipv6.value(); // get ipv6
}
return 0;
}
Remarks
The library also supports working with Unicode C++ strings such as UTF-8, UTF-16, UTF-32 and wide-char (char8_t, char16_t, char32_t and wchar_t). For strings with a base type of char, it is assumed that the input string consists of an ASCII character sequence and is not encoded with any encoding. If your application uses the Unicode encoding UTF-8 for incoming strings with a base type of char, you can define the macro IPADDRESS_CHAR_IS_UTF8. In this case, all incoming strings with a base type of char will be interpreted as UTF-8 encoding. Despite the support for Unicode, it's important to note that for successful parsing of IP addresses, the incoming string must contain valid symbols for IP addresses.
From uint/To uint
Below is code demonstrating how to create IP addresses from unsigned integers and convert them back to unsigned integers.
ipaddress defines its type uint128_t to work with 128-bit unsigned integers. It is designed to fill the gap in the C++ standard, which does not natively support 128-bit integers across all platforms. Unlike compiler-specific extensions like __int128, uint128_t ensures compatibility and portability across different compilers and architectures.
The implementation is inspired by the algorithms used in the .NET framework's UInt128, providing a reliable foundation for arithmetic operations and other integer-related functionalities.
Working with bytes
Below is an example of working with bytes and pointers to bytes directly.
The library provides many properties for determining certain features of an IP address. Let's look at the example below (all possible properties are not presented here; for a more complete picture, see the documentation for the code).
auto str5 = ip2.to_string(); // equivalent to format::compressed
auto str6 = ip2.to_string(format::full); // fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2
auto str7 = ip2.to_string(format::compact); // fe80:0:0:0:1ff:fe23:4567:890a%eth2
auto str8 = ip2.to_string(format::compressed); // fe80::1ff:fe23:4567:890a%eth2
auto reverse_pointer = ip2.reverse_pointer(); // a.0.9.8.7.6.5.4.3.2.e.f.f.f.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa
return 0;
}
IP networks
ipv4_network — A class for working with IPv4 networks.
ipv6_network — A class for working with IPv6 networks.
ip_network — Combines the ipv4_network and ipv6_network classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from ipv4_network and ipv6_network. The instance size is the same as that of ipv6_network.
Networks store two IP addresses (network address and netmask), as well as the prefix length. This means that the size of a network instance will be 2 * sizeof(<ip_type>) + sizeof(size_t).
Working with networks is similar to working with addresses. So let's get straight to the examples.
Parsing ip network strings
Let's look at examples of obtaining networks from string literals and strings.
constexprauto net6 = ip_network::parse<"1.2.3.4/255.255.255.255">(); // any version address
// Parsing with User-defined literals
constexprauto net7 = "1.2.3.0/24"_ipv4_net;
constexprauto net8 = "2001:db8::/32"_ipv6_net;
constexprauto net9 = "1.2.3.4/255.255.255.255"_net; // any version address
// Parsing at runtime. For example, when a string comes from an input
// field during execution or from any other source.
constchar* str1 = "1.2.3.0/24";
const std::string str2 = "2001:db8::/32";
auto net10 = ipv4_network::parse(str1);
auto net11 = ipv6_network::parse(str2);
auto net12 = ip_network::parse(str2); // any version address
return 0;
}
Note
If the prefixlen is not present in the input string, then the length of the prefix will be equal to the maximum length (for ipv4 it is 32, and for ipv6 it is 128)
The library provides many properties for determining certain features of an IP network. Let's look at the example below (all possible properties are not presented here; for a more complete picture, see the documentation for the code).
As you can see, getting network properties is not much different from getting address properties.
auto str2 = net2.to_string(); // equivalent to format::compressed
auto str3 = net2.to_string(format::full); // 2001:0db8:0000:0000:0000:0000:0000:0000/32
auto str4 = net2.to_string(format::compact); // 2001:db8:0:0:0:0:0:0/32
auto str5 = net2.to_string(format::compressed); // 2001:db8::/32
return 0;
}
Scope Id
The library supports Scope Id both as numeric values and as strings.
By default, Scope Id are enabled, and their maximum size is 16 characters (defined by IPADDRESS_IPV6_SCOPE_MAX_LENGTH during compilation). Yes, the scope id is internally represented as a fixed-size array, and its maximum size cannot be changed dynamically. This means that you cannot store a scope id longer than IPADDRESS_IPV6_SCOPE_MAX_LENGTH. There are several reasons for this design choice: simplicity in storing scope id, avoiding additional allocations (all data for the IP address will be as local as possible in memory), and enabling working with scope id in a constexpr manner.
Note
If you are certain that you don't need Scope Id, you can completely disable them to avoid any overhead related to their maintenance. This can be done by defining IPADDRESS_NO_IPV6_SCOPE during compilation. In this case, the instance size of ipv6_address and ip_address will be 16 bytes, and all operations related to retrieving or modifying the scope id will have no effect.
For IP addresses ipv6_address and ip_address, functionality is supported for obtaining and changing scope id.
std::cout << "scope id is integer for ip1: " << std::boolalpha << is_integer << std::endl;
std::cout << "scope id is string for ip1: " << std::boolalpha << is_string << std::endl;
std::cout << "scope id for ip1: " << scope_id.get_string() << std::endl;
}
// If IPv4 is stored in ip_address, then methods for working with the scope id have no effect
auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a");
std::cout << "has scope id for ip2: " << std::boolalpha << (bool) ip2.get_scope_id() << std::endl;
ip2.set_scope_id("123");
auto test_scope_id = ip2.get_scope_id();
if (test_scope_id) {
auto is_integer = test_scope_id.has_uint32(); // true
auto is_string = test_scope_id.has_string(); // true
std::cout << "scope id is integer for ip2: " << std::boolalpha << is_integer << std::endl;
std::cout << "scope id is string for ip2: " << std::boolalpha << is_string << std::endl;
std::cout << "scope id for ip2: " << test_scope_id.get_uint32() << std::endl;
}
return 0;
}
Remarks
By default, the use of whitespace characters in the scope id is not permitted. If a whitespace character is found in the incoming string within the scope id (for example, fe80::1ff:fe23:4567:890aet h2), an error with the code error_code::invalid_scope_id will be generated. However, if for some reason you need to parse and save spaces in the scope id, you can define the macro IPADDRESS_SCOPE_ID_SUPPORT_SPACES.
Std overrides
Note
If, for some reason, you don't want the library to overload standard functions, you can define IPADDRESS_NO_OVERLOAD_STD during compilation.
The library overloads some functions from the standard library so that IP addresses and networks (ipv4_address, ipv6_address, ip_address, ipv4_network, ipv6_network and ip_network) can be used in hash tables and other standard operations on std::swap, etc.