Skip to content

Commit f9e9310

Browse files
authored
Merge pull request #287 from cppalliance/develop
Merge to master for v1.5.0
2 parents e77d4b9 + b4a296c commit f9e9310

8 files changed

Lines changed: 317 additions & 16 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,6 @@ jobs:
298298
source_keys:
299299
- "https://apt.llvm.org/llvm-snapshot.gpg.key"
300300

301-
- toolset: clang
302-
cxxstd: "03,11,14,17,20,2b"
303-
os: macos-13
304301
- toolset: clang
305302
cxxstd: "03,11,14,17,20,2b"
306303
os: macos-14
@@ -623,7 +620,6 @@ jobs:
623620
include:
624621
- os: ubuntu-22.04
625622
- os: ubuntu-24.04
626-
- os: macos-13
627623
- os: macos-14
628624
- os: macos-15
629625

@@ -670,7 +666,6 @@ jobs:
670666
fail-fast: false
671667
matrix:
672668
include:
673-
- os: macos-13
674669
- os: macos-14
675670
- os: macos-15
676671

@@ -729,7 +724,6 @@ jobs:
729724
include:
730725
- os: ubuntu-22.04
731726
- os: ubuntu-24.04
732-
- os: macos-13
733727
- os: macos-14
734728
- os: macos-15
735729

@@ -786,7 +780,6 @@ jobs:
786780
include:
787781
- os: ubuntu-22.04
788782
- os: ubuntu-24.04
789-
- os: macos-13
790783
- os: macos-14
791784
- os: macos-15
792785

@@ -959,7 +952,6 @@ jobs:
959952
matrix:
960953
include:
961954
- os: ubuntu-24.04
962-
- os: macos-13
963955
- os: macos-14
964956
- os: macos-15
965957

doc/int128.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Matt Borland
2020

2121
include::int128/overview.adoc[]
2222

23+
include::int128/printer.adoc[]
24+
2325
include::int128/api_reference.adoc[]
2426

2527
include::int128/file_structure.adoc[]

doc/int128/printer.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
////
2+
Copyright 2025 Matt Borland
3+
Distributed under the Boost Software License, Version 1.0.
4+
https://www.boost.org/LICENSE_1_0.txt
5+
////
6+
7+
[#pretty_printer]
8+
= Pretty Printers
9+
:idprefix: pretty_printers_
10+
11+
The library contains a pretty printer for LLDB in the `extra/` folder.
12+
To use this, add the following line to your `~/.lldbinit` file:
13+
14+
[source]
15+
----
16+
command script import /path/to/int128/extra/int128_printer.py
17+
----
18+
19+
If this is successful, you should see the following message in your debugger upon startup:
20+
21+
"int128_t and uint128_t pretty printers loaded successfully"

extra/int128_printer_gdb.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Copyright 2025 Matt Borland
2+
# Distributed under the Boost Software License, Version 1.0.
3+
# https://www.boost.org/LICENSE_1_0.txt
4+
#
5+
# Struct definitions:
6+
# struct uint128_t { std::uint64_t low; std::uint64_t high; };
7+
# struct int128_t { std::uint64_t low; std::int64_t high; };
8+
#
9+
# On big endian machines the word order is reversed
10+
#
11+
# Usage: source int128_printer.py
12+
13+
import gdb
14+
import gdb.printing
15+
import re
16+
17+
class Uint128Printer:
18+
"""Pretty printer for uint128_t type"""
19+
20+
def __init__(self, val):
21+
self.val = val
22+
23+
def to_string(self):
24+
try:
25+
high = int(self.val["high"]) & 0xFFFFFFFFFFFFFFFF # Treat as unsigned
26+
low = int(self.val["low"]) & 0xFFFFFFFFFFFFFFFF
27+
28+
value = (high << 64) | low
29+
return f"{value:,}"
30+
except Exception as e:
31+
return f"<invalid uint128_t: {e}>"
32+
33+
def children(self):
34+
yield "low", self.val["low"]
35+
yield "high", self.val["high"]
36+
37+
def display_hint(self):
38+
return None
39+
40+
41+
class Int128Printer:
42+
"""Pretty printer for int128_t type"""
43+
44+
def __init__(self, val):
45+
self.val = val
46+
47+
def to_string(self):
48+
try:
49+
# high is std::int64_t (signed)
50+
high = int(self.val["high"])
51+
# Ensure high is treated as signed 64-bit
52+
if high >= 0x8000000000000000:
53+
high -= 0x10000000000000000
54+
55+
# low is std::uint64_t (unsigned)
56+
low = int(self.val["low"]) & 0xFFFFFFFFFFFFFFFF
57+
58+
value = (high << 64) + low
59+
return f"{value:,}"
60+
except Exception as e:
61+
return f"<invalid int128_t: {e}>"
62+
63+
def children(self):
64+
yield "low", self.val["low"]
65+
yield "high", self.val["high"]
66+
67+
def display_hint(self):
68+
return None
69+
70+
71+
def lookup_int128_type(val):
72+
"""
73+
Lookup function to detect if a type should use our pretty printers.
74+
Returns the appropriate printer or None.
75+
"""
76+
# Get the basic type name, stripping references and const qualifiers
77+
type_obj = val.type
78+
79+
# Handle references and pointers
80+
if type_obj.code == gdb.TYPE_CODE_REF:
81+
type_obj = type_obj.target()
82+
if type_obj.code == gdb.TYPE_CODE_PTR:
83+
return None # Don't handle pointers directly
84+
85+
# Strip const/volatile qualifiers
86+
type_obj = type_obj.unqualified()
87+
88+
type_name = str(type_obj)
89+
90+
# Patterns to match uint128_t and int128_t types
91+
uint128_pattern = re.compile(
92+
r"^(boost::int128::uint128_t|(\w+::)*uint128_t|uint128_t)$"
93+
)
94+
int128_pattern = re.compile(
95+
r"^(boost::int128::int128_t|(\w+::)*int128_t|int128_t)$"
96+
)
97+
98+
if uint128_pattern.match(type_name):
99+
return Uint128Printer(val)
100+
if int128_pattern.match(type_name):
101+
return Int128Printer(val)
102+
103+
return None
104+
105+
106+
def register_int128_printers(objfile=None):
107+
"""Register the int128 pretty printers."""
108+
if objfile is None:
109+
objfile = gdb
110+
111+
objfile.pretty_printers.append(lookup_int128_type)
112+
113+
114+
# Auto-register when the script is sourced
115+
register_int128_printers()
116+
print("int128_t and uint128_t pretty printers loaded successfully")

extra/int128_printer_lldb.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Copyright 2025 Matt Borland
2+
# Distributed under the Boost Software License, Version 1.0.
3+
# https://www.boost.org/LICENSE_1_0.txt
4+
#
5+
# Struct definitions:
6+
# struct uint128_t { std::uint64_t low; std::uint64_t high; };
7+
# struct int128_t { std::uint64_t low; std::int64_t high; };
8+
#
9+
# On big endian machines the word order is reversed
10+
11+
import lldb
12+
13+
def uint128_summary(valobj, internal_dict):
14+
"""
15+
Custom summary for uint128_t type (unsigned).
16+
Displays as decimal (base 10).
17+
"""
18+
try:
19+
val = valobj.GetNonSyntheticValue()
20+
high = val.GetChildMemberWithName("high").GetValueAsUnsigned()
21+
low = val.GetChildMemberWithName("low").GetValueAsUnsigned()
22+
23+
value = (high << 64) | low
24+
return f"{value:,}"
25+
except Exception as e:
26+
return f"<invalid uint128_t: {e}>"
27+
28+
def int128_summary(valobj, internal_dict):
29+
"""
30+
Custom summary for int128_t type (signed).
31+
Displays as decimal (base 10).
32+
"""
33+
try:
34+
val = valobj.GetNonSyntheticValue()
35+
# high is std::int64_t, so use GetValueAsSigned()
36+
high = val.GetChildMemberWithName("high").GetValueAsSigned()
37+
# low is std::uint64_t, so use GetValueAsUnsigned()
38+
low = val.GetChildMemberWithName("low").GetValueAsUnsigned()
39+
40+
value = (high << 64) + low
41+
return f"{value:,}"
42+
except Exception as e:
43+
return f"<invalid int128_t: {e}>"
44+
45+
def __lldb_init_module(debugger, internal_dict):
46+
uint128_pattern = r"^(const )?(boost::int128::uint128_t|(\w+::)*uint128_t)( &| \*)?$"
47+
int128_pattern = r"^(const )?(boost::int128::int128_t|(\w+::)*int128_t)( &| \*)?$"
48+
49+
debugger.HandleCommand(
50+
f'type summary add -x "{uint128_pattern}" -e -F int128_printer_lldb.uint128_summary'
51+
)
52+
debugger.HandleCommand(
53+
f'type synthetic add -x "{uint128_pattern}" -l int128_printer_lldb.Uint128SyntheticProvider'
54+
)
55+
56+
debugger.HandleCommand(
57+
f'type summary add -x "{int128_pattern}" -e -F int128_printer_lldb.int128_summary'
58+
)
59+
debugger.HandleCommand(
60+
f'type synthetic add -x "{int128_pattern}" -l int128_printer_lldb.Int128SyntheticProvider'
61+
)
62+
63+
print("int128_t and uint128_t pretty printers loaded successfully")
64+
65+
class Uint128SyntheticProvider:
66+
def __init__(self, valobj, internal_dict):
67+
self.valobj = valobj
68+
69+
def num_children(self):
70+
return 2
71+
72+
def get_child_index(self, name):
73+
if name == "low":
74+
return 0
75+
elif name == "high":
76+
return 1
77+
return -1
78+
79+
def get_child_at_index(self, index):
80+
if index == 0:
81+
return self.valobj.GetChildMemberWithName("low")
82+
elif index == 1:
83+
return self.valobj.GetChildMemberWithName("high")
84+
return None
85+
86+
def update(self):
87+
pass
88+
89+
def has_children(self):
90+
return True
91+
92+
class Int128SyntheticProvider:
93+
def __init__(self, valobj, internal_dict):
94+
self.valobj = valobj
95+
96+
def num_children(self):
97+
return 2
98+
99+
def get_child_index(self, name):
100+
if name == "low":
101+
return 0
102+
elif name == "high":
103+
return 1
104+
return -1
105+
106+
def get_child_at_index(self, index):
107+
if index == 0:
108+
return self.valobj.GetChildMemberWithName("low")
109+
elif index == 1:
110+
return self.valobj.GetChildMemberWithName("high")
111+
return None
112+
113+
def update(self):
114+
pass
115+
116+
def has_children(self):
117+
return True

include/boost/int128/detail/uint128_imp.hpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,10 @@ BOOST_INT128_EXPORT constexpr bool operator<(const uint128_t lhs, const uint128_
677677
}
678678
else
679679
{
680-
const uint32_t* l = reinterpret_cast<const uint32_t*>(&lhs);
681-
const uint32_t* r = reinterpret_cast<const uint32_t*>(&rhs);
680+
std::uint32_t l[4] {};
681+
std::uint32_t r[4] {};
682+
std::memcpy(l, &lhs, sizeof(lhs));
683+
std::memcpy(r, &rhs, sizeof(rhs));
682684

683685
if (l[3] != r[3])
684686
{
@@ -830,8 +832,10 @@ BOOST_INT128_EXPORT constexpr bool operator<=(const uint128_t lhs, const uint128
830832
}
831833
else
832834
{
833-
const uint32_t* l = reinterpret_cast<const uint32_t*>(&lhs);
834-
const uint32_t* r = reinterpret_cast<const uint32_t*>(&rhs);
835+
std::uint32_t l[4] {};
836+
std::uint32_t r[4] {};
837+
std::memcpy(l, &lhs, sizeof(lhs));
838+
std::memcpy(r, &rhs, sizeof(rhs));
835839

836840
if (l[3] != r[3])
837841
{
@@ -983,8 +987,10 @@ BOOST_INT128_EXPORT constexpr bool operator>(const uint128_t lhs, const uint128_
983987
}
984988
else
985989
{
986-
const uint32_t* l = reinterpret_cast<const uint32_t*>(&lhs);
987-
const uint32_t* r = reinterpret_cast<const uint32_t*>(&rhs);
990+
std::uint32_t l[4] {};
991+
std::uint32_t r[4] {};
992+
std::memcpy(l, &lhs, sizeof(lhs));
993+
std::memcpy(r, &rhs, sizeof(rhs));
988994

989995
if (l[3] != r[3])
990996
{
@@ -1136,8 +1142,10 @@ BOOST_INT128_EXPORT constexpr bool operator>=(const uint128_t lhs, const uint128
11361142
}
11371143
else
11381144
{
1139-
const uint32_t* l = reinterpret_cast<const uint32_t*>(&lhs);
1140-
const uint32_t* r = reinterpret_cast<const uint32_t*>(&rhs);
1145+
std::uint32_t l[4] {};
1146+
std::uint32_t r[4] {};
1147+
std::memcpy(l, &lhs, sizeof(lhs));
1148+
std::memcpy(r, &rhs, sizeof(rhs));
11411149

11421150
if (l[3] != r[3])
11431151
{

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ run ../examples/charconv.cpp ;
9696
run limits_link_1.cpp limits_link_2.cpp limits_link_3.cpp ;
9797

9898
# Github Issues
99+
run decimal_github_issue_1260.cpp ;
99100
run github_issue_207.cpp ;
100101
run github_issue_210.cpp ;
101102
run github_issue_221.cpp ;

0 commit comments

Comments
 (0)