Skip to content

Commit 73cf3ac

Browse files
committed
refactor(resolver): replace resolver_results wrapper with a type alias
resolver_results was a shared_ptr<vector<resolver_entry>> wrapper that gave O(1) copies and an identity-based operator==. The equality ops were never used, and the only by-value sink (connect) must own the range for coroutine safety regardless, so an rvalue is moved in cheaply either way. Make resolver_results an alias for std::vector<resolver_entry>: deletes the wrapper boilerplate and exposes the full vector interface. The one regression — deep-copying an lvalue you want to retain — is documented on resolve() and connect(), which point callers at std::move or the iterator-based connect overloads. Closes #123.
1 parent 637a90d commit 73cf3ac

8 files changed

Lines changed: 53 additions & 130 deletions

File tree

doc/modules/ROOT/pages/3.tutorials/3c.dns-lookup.adoc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ capy::task<void> do_lookup(
8585

8686
== Understanding Results
8787

88-
The resolver returns a `resolver_results` object containing `resolver_entry`
89-
elements. Each entry provides:
88+
The resolver returns a `resolver_results` — an alias for
89+
`std::vector<resolver_entry>` — containing one `resolver_entry` per resolved
90+
endpoint. Each entry provides:
9091

9192
* `get_endpoint()` — The resolved endpoint (address + port)
9293
* `host_name()` — The queried hostname

doc/modules/ROOT/pages/4.guide/4j.resolver.adoc

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,14 @@ auto [ec, results] = co_await r.resolve("127.0.0.1", "8080", flags);
117117

118118
== Working with Results
119119

120-
The `resolver_results` class is a range of `resolver_entry` objects:
120+
`resolver_results` is an alias for `std::vector<resolver_entry>`, so it
121+
supports the full `std::vector` interface (iteration, `size()`, `empty()`,
122+
indexing, and so on):
123+
124+
[source,cpp]
125+
----
126+
using resolver_results = std::vector<resolver_entry>;
127+
----
121128

122129
[source,cpp]
123130
----
@@ -134,23 +141,14 @@ for (auto const& entry : results)
134141
}
135142
----
136143

137-
=== resolver_results Interface
138-
139-
[source,cpp]
140-
----
141-
class resolver_results
142-
{
143-
public:
144-
using iterator = /* ... */;
145-
using const_iterator = /* ... */;
146-
147-
std::size_t size() const;
148-
bool empty() const;
149-
150-
const_iterator begin() const;
151-
const_iterator end() const;
152-
};
153-
----
144+
[NOTE]
145+
====
146+
Copying a `resolver_results` deep-copies every entry, and each entry owns
147+
two `std::string` query names. When handing the results to a sink that
148+
takes the range by value — such as `corosio::connect` — pass an rvalue
149+
(`std::move(results)`) or use the iterator-based overload
150+
(`connect(s, results.begin(), results.end())`) to avoid the copy.
151+
====
154152

155153
=== resolver_entry Interface
156154

include/boost/corosio/connect.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ connect(Socket& s, Iter begin, Iter end, ConnectCondition cond);
105105
@param endpoints A range of candidate endpoints. Taken by value
106106
so temporaries (e.g. `resolver_results` returned from
107107
`resolver::resolve`) remain alive for the coroutine's lifetime.
108+
Because the range is owned by the coroutine, passing an lvalue
109+
copies it; since `resolver_results` is a
110+
`std::vector<resolver_entry>`, that is a deep copy of every entry.
111+
Pass an rvalue (`std::move(results)`) or use the iterator overload
112+
(`connect(s, results.begin(), results.end())`) to avoid the copy.
108113
109114
@return An awaitable completing with
110115
`capy::io_result<typename Socket::endpoint_type>`:
@@ -153,7 +158,9 @@ connect(Socket& s, Range endpoints)
153158
154159
@param s The socket to connect. See the non-condition overload for
155160
requirements.
156-
@param endpoints A range of candidate endpoints.
161+
@param endpoints A range of candidate endpoints, taken by value. See
162+
the non-condition overload for the deep-copy caveat when passing
163+
an lvalue `resolver_results`.
157164
@param cond A predicate invocable with
158165
`(std::error_code const&, typename Socket::endpoint_type const&)`
159166
returning a value contextually convertible to `bool`.

include/boost/corosio/native/detail/iocp/win_resolver_service.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//
22
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
33
// Copyright (c) 2026 Steve Gerbino
4+
// Copyright (c) 2026 Michael Vandeberg
45
//
56
// Distributed under the Boost Software License, Version 1.0. (See accompanying
67
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -203,7 +204,7 @@ convert_results(
203204
}
204205
}
205206

206-
return resolver_results(std::move(entries));
207+
return entries;
207208
}
208209

209210
} // namespace resolver_detail

include/boost/corosio/native/detail/posix/posix_resolver_service.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2026 Steve Gerbino
3+
// Copyright (c) 2026 Michael Vandeberg
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)
@@ -161,7 +162,7 @@ posix_resolver_detail::convert_results(
161162
}
162163
}
163164

164-
return resolver_results(std::move(entries));
165+
return entries;
165166
}
166167

167168
inline std::error_code

include/boost/corosio/native/native_resolver.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2026 Steve Gerbino
3+
// Copyright (c) 2026 Michael Vandeberg
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)
@@ -191,6 +192,9 @@ class native_resolver : public resolver
191192
@param service The service name or port string.
192193
193194
@return An awaitable yielding `io_result<resolver_results>`.
195+
196+
@note `resolver_results` is an alias for `std::vector<resolver_entry>`;
197+
copying it deep-copies every entry. See @ref resolver::resolve.
194198
*/
195199
auto resolve(std::string_view host, std::string_view service)
196200
{

include/boost/corosio/resolver.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//
22
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
33
// Copyright (c) 2026 Steve Gerbino
4+
// Copyright (c) 2026 Michael Vandeberg
45
//
56
// Distributed under the Boost Software License, Version 1.0. (See accompanying
67
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -351,6 +352,11 @@ class BOOST_COROSIO_DECL resolver : public io_object
351352
352353
@return An awaitable that completes with `io_result<resolver_results>`.
353354
355+
@note `resolver_results` is an alias for `std::vector<resolver_entry>`.
356+
Copying it deep-copies every entry (each owns two `std::string`s);
357+
move it (`std::move(results)`) or pass iterators when handing it to
358+
a by-value sink such as @ref connect.
359+
354360
@par Example
355361
@code
356362
auto [ec, results] = co_await r.resolve("www.example.com", "https");

include/boost/corosio/resolver_results.hpp

Lines changed: 12 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3+
// Copyright (c) 2026 Michael Vandeberg
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)
@@ -13,8 +14,6 @@
1314
#include <boost/corosio/detail/config.hpp>
1415
#include <boost/corosio/endpoint.hpp>
1516

16-
#include <cstddef>
17-
#include <memory>
1817
#include <string>
1918
#include <string_view>
2019
#include <vector>
@@ -80,115 +79,21 @@ class resolver_entry
8079

8180
/** A range of entries produced by a resolver.
8281
83-
This class holds the results of a DNS resolution query.
84-
It provides a range interface for iterating over the
85-
resolved endpoints.
82+
This is an alias for `std::vector<resolver_entry>`: a contiguous,
83+
owning range of the endpoints resolved by a query. It supports the
84+
full `std::vector` interface (iteration, `size()`, `empty()`, etc.).
85+
86+
@note Copying a `resolver_results` deep-copies every entry, and each
87+
entry owns two `std::string`s (the host and service names). When you
88+
want to hand a result to a sink that takes the range by value — such
89+
as `corosio::connect` — pass an rvalue (`std::move(results)`) or use
90+
the iterator-based `connect` overloads to avoid the copy.
8691
8792
@par Thread Safety
8893
Distinct objects: Safe.@n
89-
Shared objects: Safe (immutable after construction).
94+
Shared objects: Unsafe.
9095
*/
91-
class resolver_results
92-
{
93-
public:
94-
/// The entry type.
95-
using value_type = resolver_entry;
96-
97-
/// Const reference to an entry.
98-
using const_reference = value_type const&;
99-
100-
/// Reference to an entry (always const).
101-
using reference = const_reference;
102-
103-
/// Const iterator over entries.
104-
using const_iterator = std::vector<resolver_entry>::const_iterator;
105-
106-
/// Iterator over entries (always const).
107-
using iterator = const_iterator;
108-
109-
/// Signed difference type.
110-
using difference_type = std::ptrdiff_t;
111-
112-
/// Unsigned size type.
113-
using size_type = std::size_t;
114-
115-
private:
116-
std::shared_ptr<std::vector<resolver_entry>> entries_;
117-
118-
public:
119-
/// Construct an empty results range.
120-
resolver_results() = default;
121-
122-
/** Construct from a vector of entries.
123-
124-
@param entries The resolved entries.
125-
*/
126-
explicit resolver_results(std::vector<resolver_entry> entries)
127-
: entries_(
128-
std::make_shared<std::vector<resolver_entry>>(std::move(entries)))
129-
{
130-
}
131-
132-
/// Return the number of entries.
133-
size_type size() const noexcept
134-
{
135-
return entries_ ? entries_->size() : 0;
136-
}
137-
138-
/// Check if the results are empty.
139-
bool empty() const noexcept
140-
{
141-
return !entries_ || entries_->empty();
142-
}
143-
144-
/// Return an iterator to the first entry.
145-
const_iterator begin() const noexcept
146-
{
147-
if (entries_)
148-
return entries_->begin();
149-
return std::vector<resolver_entry>::const_iterator();
150-
}
151-
152-
/// Return an iterator past the last entry.
153-
const_iterator end() const noexcept
154-
{
155-
if (entries_)
156-
return entries_->end();
157-
return std::vector<resolver_entry>::const_iterator();
158-
}
159-
160-
/// Return an iterator to the first entry.
161-
const_iterator cbegin() const noexcept
162-
{
163-
return begin();
164-
}
165-
166-
/// Return an iterator past the last entry.
167-
const_iterator cend() const noexcept
168-
{
169-
return end();
170-
}
171-
172-
/// Swap with another results object.
173-
void swap(resolver_results& other) noexcept
174-
{
175-
entries_.swap(other.entries_);
176-
}
177-
178-
/// Test for equality.
179-
friend bool
180-
operator==(resolver_results const& a, resolver_results const& b) noexcept
181-
{
182-
return a.entries_ == b.entries_;
183-
}
184-
185-
/// Test for inequality.
186-
friend bool
187-
operator!=(resolver_results const& a, resolver_results const& b) noexcept
188-
{
189-
return !(a == b);
190-
}
191-
};
96+
using resolver_results = std::vector<resolver_entry>;
19297

19398
/** The result of a reverse DNS resolution.
19499

0 commit comments

Comments
 (0)