Flow 1.0.1
Flow project: Full implementation reference.
endpoint.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
23#include <boost/asio.hpp>
24#include <boost/unordered_set.hpp>
25#include <ostream>
26
27namespace flow::net_flow
28{
29// Types.
30
31/**
32 * Represents the remote endpoint of a Flow-protocol connection; identifies the UDP endpoint of
33 * the remote Node and the logical Flow-protocol port within that Node. In particular when performing
34 * Node::connect(), one must supply a Remote_endpoint. Constuct this via direct member initialization.
35 *
36 * When performing Node::listen(), on the other hand, a Remote_endpoint is unnecessary; the UDP
37 * endpoint has already been supplied when starting the overall Node, so only the Flow-protocol port
38 * number is needed by Node::listen().
39 *
40 * Since the components of a Remote_endpoint should be freely readable and modifiable, I forewent
41 * the usual accessors/mutators and simply made those components public data members.
42 *
43 * @see As of this writing, class Node documentation header has a lengthy discussion about whether
44 * Remote_endpoint needs #m_flow_port at all; if it does not, then Remote_endpoint becomes isomorphic
45 * to util::Udp_endpoint and can be thus aliased or eliminated. See the to-dos section of that large doc
46 * header.
47 * (That discussion has implications for all of `net_flow`, so it belongs there, not here on this humble
48 * `struct`.)
49 *
50 * @internal
51 *
52 * @todo The respected Scott Meyers ("Effective C++") would recommend that Remote_endpoint::hash() and
53 * Remote_endpoint::operator==() be written as free functions instead of as `struct` members (see
54 * http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197). The same would apply
55 * to a potentially great number of other non-`virtual` methods (including operators) of other `struct`s and classes
56 * that could be implemented without `friend`, so really pursuing this approach could touch quite a few things.
57 * I won't repeat Meyers' reasoning; see the link. I find the reasoning mostly compelling.
58 * To summarize his final suggestions: An operation on a type should be a free function if ALL of the
59 * following holds: it is not `virtual` by nature; AND: it is `<<` or `>>`, and/or its would-be left operand would
60 * require type conversions, and/or it can be *implemented* entirely via the type's publicly exposed interface.
61 * We already follow the advice for `<<` and `>>` (if only because we apply it to stream ops, and for that it just
62 * makes sense, `<<` especially, since the stream is the left-most operand there, so it *has* to be a free function
63 * in that case -- and thus also for `>>` for consistency). The type conversion is not a common
64 * thing for this library; so that leaves non-`virtual` operations (which is most of them) that can be implemented via
65 * public APIs only (which is probaby common for `struct`s like Remote_endpoint, though we generally try not to
66 * have lots of `struct` methods in the first place... usually). Before rushing headlong into this project, consider
67 * a few more things, though. 1, Doxygen wouldn't pick up the relation between a non-friend free function dealing
68 * with `struct` or class `C` and `C` itself; so verbosity/error-proneness would increase by having to add a Doxygen
69 * `relatesalso` special command to each free function; otherwise documentation becomes less readable (and there's no
70 * way to enforce this by having Doxygen fail without this being done, somehow). 2, in the
71 * `C`-returning-static-member-of-`C` pattern, usually in our code it would call a private `C` constructor,
72 * meaning it would require `friend` to make it a free function, meaning it breaks Meyers' own rule and thus should
73 * be left a member. 3, Meyers tends to place very little value on brevity as its own virtue. If you find that
74 * following the above rule in some case seems to be significantly increasing code complexity/size, maybe it's best
75 * to leave it alone. (I am thinking of Low_lvl_packet and its sub-types like Ack_packet: they are `struct`s but
76 * not typical ones, with complex APIs; making those APIs free function when non-`virtual` sounds like a rather hairy
77 * change that "feels" like it would reduce simplicity and may increase size, at least due to all the necessary
78 * 'splaining in the comments.) All that said, this is perhaps worth pursuing (in the pursuit of stylistic
79 * perfection) -- but surgically and first-do-no-harm-edly. Oh, also, constructors don't count for this and should
80 * continue to remain constructors and not some free functions stuff (should be obvious why, but just in case you get
81 * any ideas...).
82 *
83 * @todo There is a sub-case of the immediately preceding to-do that may be performed first without much
84 * controversy. That is the case where there is some member method of some type, that is then called by an
85 * extremely thin wrapper free function (not even a `friend`). In fact, `hash_value()` and Remote_endpoint::hash()
86 * are such a pair. Assuming one does not forget Doxygen's `relatesalso` command, it would be easy and concise
87 * to just get rid of the member and simply move its implementation directly into the free function. After all,
88 * people are meant to use the free function anyway, so why the middle-man method? In this example,
89 * `hash_value()` would directly compute the hash, and Remote_endpoint::hash() would not exist; but the former
90 * would include a Doxygen `relatesalso Remote_endpoint` command to ensure properly linked generated documentation.
91 */
93{
94 // Data.
95
96 /// UDP address (IP address/UDP port) where the Node identified by this endpoint bound its low-level UDP socket.
98 /// The logical Flow port within the Node for the particular connection identified by this endpoint.
100
101 // Methods.
102
103 /**
104 * Hash value of this Remote_endpoint for `unordered_*<>`.
105 *
106 * @return Ditto.
107 */
108 size_t hash() const;
109}; // class Remote_endpoint
110
111// Free functions: in *_fwd.hpp.
112
113} // namespace flow::net_flow
Flow module containing the API and implementation of the Flow network protocol, a TCP-inspired stream...
Definition: node.cpp:25
uint16_t flow_port_t
Logical Flow port type (analogous to a UDP/TCP port in spirit but in no way relevant to UDP/TCP).
boost::asio::ip::udp::endpoint Udp_endpoint
Short-hand for the UDP endpoint (IP/port) type.
Definition: util_fwd.hpp:208
Represents the remote endpoint of a Flow-protocol connection; identifies the UDP endpoint of the remo...
Definition: endpoint.hpp:93
size_t hash() const
Hash value of this Remote_endpoint for unordered_*<>.
Definition: endpoint.cpp:24
util::Udp_endpoint m_udp_endpoint
UDP address (IP address/UDP port) where the Node identified by this endpoint bound its low-level UDP ...
Definition: endpoint.hpp:97
flow_port_t m_flow_port
The logical Flow port within the Node for the particular connection identified by this endpoint.
Definition: endpoint.hpp:99