1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #pragma once
8
9 /**
10 * \file
11 * Contains the declarations of a base class for all TL-objects and some helper methods
12 */
13
14 #include <cstddef>
15 #include <cstdint>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19
20 namespace td {
21
22 class TlStorerCalcLength;
23
24 class TlStorerUnsafe;
25
26 class TlStorerToString;
27
28 /**
29 * This class is a base class for all TDLib TL-objects.
30 */
31 class TlObject {
32 public:
33 /**
34 * Returns an identifier, uniquely determining the TL-type of the object.
35 */
36 virtual std::int32_t get_id() const = 0;
37
38 /**
39 * Appends the object to the storer serializing object, a buffer of fixed length.
40 * \param[in] s Storer to which the object will be appended.
41 */
store(TlStorerUnsafe & s)42 virtual void store(TlStorerUnsafe &s) const {
43 }
44
45 /**
46 * Appends the object to the storer, calculating the TL-length of the serialized object.
47 * \param[in] s Storer to which the object will be appended.
48 */
store(TlStorerCalcLength & s)49 virtual void store(TlStorerCalcLength &s) const {
50 }
51
52 /**
53 * Helper function for the to_string method. Appends a string representation of the object to the storer.
54 * \param[in] s Storer to which the object string representation will be appended.
55 * \param[in] field_name Object field_name if applicable.
56 */
57 virtual void store(TlStorerToString &s, const char *field_name) const = 0;
58
59 /**
60 * Default constructor.
61 */
62 TlObject() = default;
63
64 /**
65 * Deleted copy constructor.
66 */
67 TlObject(const TlObject &) = delete;
68
69 /**
70 * Deleted copy assignment operator.
71 */
72 TlObject &operator=(const TlObject &) = delete;
73
74 /**
75 * Default move constructor.
76 */
77 TlObject(TlObject &&) = default;
78
79 /**
80 * Default move assignment operator.
81 */
82 TlObject &operator=(TlObject &&) = default;
83
84 /**
85 * Virtual destructor.
86 */
87 virtual ~TlObject() = default;
88 };
89
90 /// @cond UNDOCUMENTED
91 namespace tl {
92
93 template <class T>
94 class unique_ptr {
95 public:
96 using pointer = T *;
97 using element_type = T;
98
99 unique_ptr() noexcept = default;
100 unique_ptr(const unique_ptr &other) = delete;
101 unique_ptr &operator=(const unique_ptr &other) = delete;
unique_ptr(unique_ptr && other)102 unique_ptr(unique_ptr &&other) noexcept : ptr_(other.release()) {
103 }
104 unique_ptr &operator=(unique_ptr &&other) noexcept {
105 reset(other.release());
106 return *this;
107 }
~unique_ptr()108 ~unique_ptr() {
109 reset();
110 }
111
unique_ptr(std::nullptr_t)112 unique_ptr(std::nullptr_t) noexcept {
113 }
unique_ptr(T * ptr)114 explicit unique_ptr(T *ptr) noexcept : ptr_(ptr) {
115 }
116 template <class S, class = typename std::enable_if<std::is_base_of<T, S>::value>::type>
unique_ptr(unique_ptr<S> && other)117 unique_ptr(unique_ptr<S> &&other) noexcept : ptr_(static_cast<S *>(other.release())) {
118 }
119 template <class S, class = typename std::enable_if<std::is_base_of<T, S>::value>::type>
120 unique_ptr &operator=(unique_ptr<S> &&other) noexcept {
121 reset(static_cast<T *>(other.release()));
122 return *this;
123 }
124 void reset(T *new_ptr = nullptr) noexcept {
125 static_assert(sizeof(T) > 0, "Can't destroy unique_ptr with incomplete type");
126 delete ptr_;
127 ptr_ = new_ptr;
128 }
release()129 T *release() noexcept {
130 auto res = ptr_;
131 ptr_ = nullptr;
132 return res;
133 }
get()134 T *get() noexcept {
135 return ptr_;
136 }
get()137 const T *get() const noexcept {
138 return ptr_;
139 }
140 T *operator->() noexcept {
141 return ptr_;
142 }
143 const T *operator->() const noexcept {
144 return ptr_;
145 }
146 T &operator*() noexcept {
147 return *ptr_;
148 }
149 const T &operator*() const noexcept {
150 return *ptr_;
151 }
152 explicit operator bool() const noexcept {
153 return ptr_ != nullptr;
154 }
155
156 private:
157 T *ptr_{nullptr};
158 };
159
160 template <class T>
161 bool operator==(std::nullptr_t, const unique_ptr<T> &p) {
162 return !p;
163 }
164 template <class T>
165 bool operator==(const unique_ptr<T> &p, std::nullptr_t) {
166 return !p;
167 }
168 template <class T>
169 bool operator!=(std::nullptr_t, const unique_ptr<T> &p) {
170 return static_cast<bool>(p);
171 }
172 template <class T>
173 bool operator!=(const unique_ptr<T> &p, std::nullptr_t) {
174 return static_cast<bool>(p);
175 }
176
177 } // namespace tl
178 /// @endcond
179
180 /**
181 * A smart wrapper to store a pointer to a TL-object.
182 */
183 template <class Type>
184 using tl_object_ptr = tl::unique_ptr<Type>;
185
186 /**
187 * A function to create a dynamically allocated TL-object. Can be treated as an analogue of std::make_unique.
188 * Usage example:
189 * \code
190 * auto get_authorization_state_request = td::make_tl_object<td::td_api::getAuthorizationState>();
191 * auto message_text = td::make_tl_object<td::td_api::formattedText>("Hello, world!!!",
192 * td::td_api::array<td::tl_object_ptr<td::td_api::textEntity>>());
193 * auto send_message_request = td::make_tl_object<td::td_api::sendMessage>(chat_id, 0, 0, nullptr, nullptr,
194 * td::make_tl_object<td::td_api::inputMessageText>(std::move(message_text), false, true));
195 * \endcode
196 *
197 * \tparam Type Type of the TL-object to construct.
198 * \param[in] args Arguments to pass to the object constructor.
199 * \return Wrapped pointer to the created TL-object.
200 */
201 template <class Type, class... Args>
make_tl_object(Args &&...args)202 tl_object_ptr<Type> make_tl_object(Args &&... args) {
203 return tl_object_ptr<Type>(new Type(std::forward<Args>(args)...));
204 }
205
206 /**
207 * A function to downcast a wrapped pointer to a TL-object to a pointer to its subclass.
208 * Casting an object to an incorrect type will lead to undefined behaviour.
209 * Examples of usage:
210 * \code
211 * td::tl_object_ptr<td::td_api::callState> call_state = ...;
212 * switch (call_state->get_id()) {
213 * case td::td_api::callStatePending::ID: {
214 * auto state = td::move_tl_object_as<td::td_api::callStatePending>(call_state);
215 * // use state
216 * break;
217 * }
218 * case td::td_api::callStateExchangingKeys::ID: {
219 * // no additional fields, so cast isn't needed
220 * break;
221 * }
222 * case td::td_api::callStateReady::ID: {
223 * auto state = td::move_tl_object_as<td::td_api::callStateReady>(call_state);
224 * // use state
225 * break;
226 * }
227 * case td::td_api::callStateHangingUp::ID: {
228 * // no additional fields, so cast isn't needed
229 * break;
230 * }
231 * case td::td_api::callStateDiscarded::ID: {
232 * auto state = td::move_tl_object_as<td::td_api::callStateDiscarded>(call_state);
233 * // use state
234 * break;
235 * }
236 * case td::td_api::callStateError::ID: {
237 * auto state = td::move_tl_object_as<td::td_api::callStateError>(call_state);
238 * // use state
239 * break;
240 * }
241 * default:
242 * assert(false);
243 * }
244 * \endcode
245 *
246 * \tparam ToT Type of a TL-object to move to.
247 * \tparam FromT Type of a TL-object to move from, this is auto-deduced.
248 * \param[in] from Wrapped pointer to a TL-object.
249 */
250 template <class ToT, class FromT>
move_tl_object_as(tl_object_ptr<FromT> & from)251 tl_object_ptr<ToT> move_tl_object_as(tl_object_ptr<FromT> &from) {
252 return tl_object_ptr<ToT>(static_cast<ToT *>(from.release()));
253 }
254
255 /**
256 * \overload
257 */
258 template <class ToT, class FromT>
move_tl_object_as(tl_object_ptr<FromT> && from)259 tl_object_ptr<ToT> move_tl_object_as(tl_object_ptr<FromT> &&from) {
260 return tl_object_ptr<ToT>(static_cast<ToT *>(from.release()));
261 }
262
263 } // namespace td
264