Skip to content

Commit 0a2c39f

Browse files
committed
feat: constexpr URL parsing for C++20
fix #890
1 parent 293621f commit 0a2c39f

File tree

159 files changed

+3893
-2965
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+3893
-2965
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ x64
99

1010
cmake-build-*/
1111
cmake-build/
12+
build/*
13+
!build/Jamfile
14+
local/
15+
tmp/
1216
AGENTS.md
17+
CLAUDE.md
1318
.roadmap/
1419
.idea/
1520
doc/modules/reference/

doc/modules/ROOT/examples/unit/snippets.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,38 @@ parsing_urls()
684684
// end::snippet_parsing_url_3[]
685685
BOOST_TEST(v.buffer() == "/path/to/file.txt#anchor");
686686
}
687+
688+
#if defined(BOOST_URL_HAS_CXX20_CONSTEXPR)
689+
{
690+
// tag::snippet_constexpr_parsing_1[]
691+
// Parse and validate a URL at compile time (C++20).
692+
// value() on an error produces a compile error,
693+
// so invalid URLs are caught at build time.
694+
constexpr url_view api_base =
695+
parse_uri("https://api.example.com/v2").value();
696+
697+
// A malformed literal would fail to compile:
698+
// constexpr url_view bad =
699+
// parse_uri("ht tp://bad url").value();
700+
// end::snippet_constexpr_parsing_1[]
701+
BOOST_TEST(api_base.scheme() == "https");
702+
}
703+
{
704+
// tag::snippet_constexpr_parsing_2[]
705+
// Pre-parsed URL constants have zero runtime cost.
706+
// Parsing happens entirely at compile time.
707+
constexpr url_view default_endpoint =
708+
parse_uri("https://api.example.com:8443/v1").value();
709+
constexpr url_view fallback_endpoint =
710+
parse_uri("http://localhost:9090/debug").value();
711+
712+
// Components are available at runtime with no parsing overhead
713+
assert(default_endpoint.port_number() == 8443);
714+
assert(fallback_endpoint.scheme() == "http");
715+
// end::snippet_constexpr_parsing_2[]
716+
BOOST_TEST(default_endpoint.port_number() == 8443);
717+
}
718+
#endif
687719
}
688720

689721
void

doc/modules/ROOT/pages/urls/parsing.adoc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,3 +246,26 @@ include::example$unit/snippets.cpp[tag=snippet_parsing_url_1b,indent=0]
246246
Check the reference for cpp:result[] for a synopsis of the type.
247247

248248

249+
== Constexpr Parsing
250+
251+
In {cpp}20 and later, all parse functions are `constexpr`.
252+
This means URLs can be parsed and validated at compile time, which is useful when one side of a comparison is a fixed string that should be pre-parsed and checked for correctness.
253+
254+
For instance, a `constexpr` cpp:url_view[] parsed from a valid string literal is fully resolved at compile time.
255+
If the literal is malformed, `value()` attempts to throw, which is not permitted during constant evaluation, so the program fails to compile:
256+
257+
[source,cpp]
258+
----
259+
include::example$unit/snippets.cpp[tag=snippet_constexpr_parsing_1,indent=0]
260+
----
261+
262+
Pre-parsed `constexpr` URL views can also serve as zero-cost constants.
263+
All parsing is done at compile time; at runtime the components are accessed with no parsing overhead:
264+
265+
[source,cpp]
266+
----
267+
include::example$unit/snippets.cpp[tag=snippet_constexpr_parsing_2,indent=0]
268+
----
269+
270+
Because cpp:url_view[] does not own its character buffer, `constexpr` URL views always reference string literals.
271+

include/boost/url/authority_view.hpp

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3+
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See accompanying
56
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -25,6 +26,15 @@
2526
namespace boost {
2627
namespace urls {
2728

29+
class url_base;
30+
31+
namespace implementation_defined {
32+
struct authority_rule_t;
33+
struct uri_rule_t;
34+
struct relative_ref_rule_t;
35+
struct absolute_uri_rule_t;
36+
} // implementation_defined
37+
2838
/** A non-owning reference to a valid authority
2939
3040
Objects of this type represent valid authority
@@ -79,17 +89,27 @@ namespace urls {
7989
@see
8090
@ref parse_authority.
8191
*/
82-
class BOOST_URL_DECL
92+
class BOOST_SYMBOL_VISIBLE
8393
authority_view
8494
: private detail::parts_base
8595
{
8696
detail::url_impl u_;
8797

8898
friend struct detail::url_impl;
89-
99+
friend struct implementation_defined::authority_rule_t;
100+
friend struct implementation_defined::uri_rule_t;
101+
friend struct implementation_defined::relative_ref_rule_t;
102+
friend struct implementation_defined::absolute_uri_rule_t;
103+
friend class url_view_base;
104+
friend class url_base;
105+
106+
BOOST_URL_CXX14_CONSTEXPR
90107
explicit
91108
authority_view(
92-
detail::url_impl const& u) noexcept;
109+
detail::url_impl const& u) noexcept
110+
: u_(u)
111+
{
112+
}
93113

94114
public:
95115
//--------------------------------------------
@@ -100,8 +120,15 @@ class BOOST_URL_DECL
100120

101121
/** Destructor
102122
*/
123+
BOOST_URL_CXX20_CONSTEXPR
103124
virtual
104-
~authority_view();
125+
~authority_view()
126+
{
127+
// Empty body instead of = default because
128+
// some GCC versions reject a defaulted
129+
// virtual constexpr destructor in constexpr
130+
// evaluation ("used before its definition").
131+
}
105132

106133
/** Constructor
107134
@@ -115,7 +142,11 @@ class BOOST_URL_DECL
115142
116143
@par Specification
117144
*/
118-
authority_view() noexcept;
145+
BOOST_URL_CXX14_CONSTEXPR
146+
authority_view() noexcept
147+
: u_(from::authority)
148+
{
149+
}
119150

120151
/** Construct from a string.
121152
@@ -154,13 +185,15 @@ class BOOST_URL_DECL
154185
@see
155186
@ref parse_authority.
156187
*/
188+
BOOST_URL_CXX20_CONSTEXPR
157189
explicit
158190
authority_view(core::string_view s);
159191

160192
/** Constructor
161193
*/
194+
BOOST_URL_CXX14_CONSTEXPR
162195
authority_view(
163-
authority_view const&) noexcept;
196+
authority_view const&) noexcept = default;
164197

165198
/** Assignment
166199
@@ -173,9 +206,10 @@ class BOOST_URL_DECL
173206
@par Exception Safety
174207
Throws nothing.
175208
*/
209+
BOOST_URL_CXX20_CONSTEXPR
176210
authority_view&
177211
operator=(
178-
authority_view const& other) noexcept;
212+
authority_view const& other) noexcept = default;
179213

180214
//--------------------------------------------
181215
//
@@ -317,6 +351,7 @@ class BOOST_URL_DECL
317351
@ref userinfo.
318352
319353
*/
354+
BOOST_URL_CXX20_CONSTEXPR
320355
bool
321356
has_userinfo() const noexcept;
322357

@@ -416,6 +451,7 @@ class BOOST_URL_DECL
416451
@ref user,
417452
@ref userinfo.
418453
*/
454+
BOOST_URL_CXX20_CONSTEXPR
419455
pct_string_view
420456
encoded_userinfo() const noexcept;
421457

@@ -519,6 +555,7 @@ class BOOST_URL_DECL
519555
@ref user,
520556
@ref userinfo.
521557
*/
558+
BOOST_URL_CXX20_CONSTEXPR
522559
pct_string_view
523560
encoded_user() const noexcept;
524561

@@ -562,6 +599,7 @@ class BOOST_URL_DECL
562599
@ref user,
563600
@ref userinfo.
564601
*/
602+
BOOST_URL_CXX20_CONSTEXPR
565603
bool
566604
has_password() const noexcept;
567605

@@ -659,6 +697,7 @@ class BOOST_URL_DECL
659697
@ref user,
660698
@ref userinfo.
661699
*/
700+
BOOST_URL_CXX20_CONSTEXPR
662701
pct_string_view
663702
encoded_password() const noexcept;
664703

@@ -782,6 +821,7 @@ class BOOST_URL_DECL
782821
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
783822
>3.2.2. Host (rfc3986)</a>
784823
*/
824+
BOOST_URL_CXX20_CONSTEXPR
785825
pct_string_view
786826
encoded_host() const noexcept;
787827

@@ -901,6 +941,7 @@ class BOOST_URL_DECL
901941
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
902942
>3.2.2. Host (rfc3986)</a>
903943
*/
944+
BOOST_URL_CXX20_CONSTEXPR
904945
pct_string_view
905946
encoded_host_address() const noexcept;
906947

@@ -942,6 +983,7 @@ class BOOST_URL_DECL
942983
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
943984
>3.2.2. Host (rfc3986)</a>
944985
*/
986+
BOOST_URL_CXX20_CONSTEXPR
945987
ipv4_address
946988
host_ipv4_address() const noexcept;
947989

@@ -991,6 +1033,7 @@ class BOOST_URL_DECL
9911033
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
9921034
>3.2.2. Host (rfc3986)</a>
9931035
*/
1036+
BOOST_URL_CXX20_CONSTEXPR
9941037
ipv6_address
9951038
host_ipv6_address() const noexcept;
9961039

@@ -1025,6 +1068,7 @@ class BOOST_URL_DECL
10251068
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
10261069
>3.2.2. Host (rfc3986)</a>
10271070
*/
1071+
BOOST_URL_CXX20_CONSTEXPR
10281072
core::string_view
10291073
host_ipvfuture() const noexcept;
10301074

@@ -1112,6 +1156,7 @@ class BOOST_URL_DECL
11121156
@li <a href="https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2"
11131157
>3.2.2. Host (rfc3986)</a>
11141158
*/
1159+
BOOST_URL_CXX20_CONSTEXPR
11151160
pct_string_view
11161161
encoded_host_name() const noexcept;
11171162

@@ -1155,6 +1200,7 @@ class BOOST_URL_DECL
11551200
@ref port,
11561201
@ref port_number.
11571202
*/
1203+
BOOST_URL_CXX20_CONSTEXPR
11581204
bool
11591205
has_port() const noexcept;
11601206

@@ -1192,6 +1238,7 @@ class BOOST_URL_DECL
11921238
@ref has_port,
11931239
@ref port_number.
11941240
*/
1241+
BOOST_URL_CXX20_CONSTEXPR
11951242
core::string_view
11961243
port() const noexcept;
11971244

@@ -1229,6 +1276,7 @@ class BOOST_URL_DECL
12291276
@ref has_port,
12301277
@ref port.
12311278
*/
1279+
BOOST_URL_CXX20_CONSTEXPR
12321280
std::uint16_t
12331281
port_number() const noexcept;
12341282

@@ -1270,6 +1318,7 @@ class BOOST_URL_DECL
12701318
12711319
@return The host and port
12721320
*/
1321+
BOOST_URL_CXX20_CONSTEXPR
12731322
pct_string_view
12741323
encoded_host_and_port() const noexcept;
12751324

@@ -1537,14 +1586,19 @@ operator<<(
15371586
@see
15381587
@ref authority_view.
15391588
*/
1540-
BOOST_URL_DECL
1589+
BOOST_URL_CXX20_CONSTEXPR
15411590
system::result<authority_view>
15421591
parse_authority(
15431592
core::string_view s) noexcept;
15441593

1545-
//------------------------------------------------
1546-
15471594
} // urls
15481595
} // boost
15491596

1597+
// When rfc/authority_rule.hpp is being processed,
1598+
// it will include impl/authority_view.hpp itself
1599+
// after declaring authority_rule.
1600+
#if !defined(BOOST_URL_RFC_AUTHORITY_RULE_HPP)
1601+
#include <boost/url/impl/authority_view.hpp>
1602+
#endif
1603+
15501604
#endif

include/boost/url/detail/any_params_iter.hpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ struct BOOST_SYMBOL_VISIBLE
5454
// True if the sequence is empty
5555
bool empty = false;
5656

57-
BOOST_URL_DECL
5857
virtual
5958
~any_params_iter() noexcept = 0;
6059

@@ -93,7 +92,6 @@ struct BOOST_SYMBOL_VISIBLE
9392
: any_params_iter
9493
{
9594
// ne = never empty
96-
BOOST_URL_DECL
9795
explicit
9896
query_string_iter(
9997
core::string_view s,
@@ -154,14 +152,12 @@ struct params_iter_base
154152
{}
155153

156154
// return encoded size
157-
BOOST_URL_DECL
158155
void
159156
measure_impl(
160157
std::size_t& n,
161158
param_view const& p) noexcept;
162159

163160
// encode to dest
164-
BOOST_URL_DECL
165161
void
166162
copy_impl(
167163
char*& dest,
@@ -264,14 +260,12 @@ struct BOOST_SYMBOL_VISIBLE
264260
struct params_encoded_iter_base
265261
{
266262
protected:
267-
BOOST_URL_DECL
268263
static
269264
void
270265
measure_impl(
271266
std::size_t& n,
272267
param_view const& v) noexcept;
273268

274-
BOOST_URL_DECL
275269
static
276270
void
277271
copy_impl(
@@ -430,4 +424,6 @@ make_params_encoded_iter(
430424
} // urls
431425
} // boost
432426

427+
#include <boost/url/detail/impl/any_params_iter.hpp>
428+
433429
#endif

0 commit comments

Comments
 (0)